Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Acf Pro Stubs Laravel Package

php-stubs/acf-pro-stubs

PHP stubs for Advanced Custom Fields Pro, providing accurate function and class signatures so IDEs and static analysis tools like PHPStan and Psalm can offer autocompletion and type checking for ACF Pro APIs.

View on GitHub
Deep Wiki
Context7

Getting Started

php-stubs/acf-pro-stubs is not runtime code — it is a single generated/curated PHP file that teaches static analysers and IDEs about the ACF PRO API (get_field, have_rows, acf_add_local_field_group, acf_register_block_type, the ACF class, etc.). Nothing is autoloaded, nothing ships to production, and it changes no behaviour.

Minimal setup (3 steps):

composer require --dev php-stubs/acf-pro-stubs php-stubs/wordpress-stubs
# phpstan.neon
includes:
    - vendor/szepeviktor/phpstan-wordpress/extension.neon   # optional but recommended
parameters:
    level: 6
    paths: [src, theme]
    scanFiles:
        - vendor/php-stubs/wordpress-stubs/wordpress-stubs.php
        - vendor/php-stubs/acf-pro-stubs/acf-pro-stubs.php
vendor/bin/phpstan analyse

Psalm users instead put the file under <stubs><file name="vendor/php-stubs/acf-pro-stubs/acf-pro-stubs.php"/></stubs>.

Where to look first:

  • vendor/php-stubs/acf-pro-stubs/acf-pro-stubs.php — the whole package. Grep it for the function you are unsure about; if it is not there, analysis will not know it.
  • The repo README for the exact scanFiles snippet for your analyser.
  • vendor/php-stubs/wordpress-stubs/wordpress-stubs.php alongside it — ACF stubs reference WP types (WP_Post, WP_Term) that come from that package.

First use case: a theme or plugin that calls ACF functions, analysed in CI without WordPress installed. Before this package, PHPStan reports Function get_field not found on every template. After adding the two scanFiles entries, those disappear and real errors (wrong argument counts, typos like get_feild) surface.

Implementation Patterns

Wrap get_field() — do not fight mixed every time. ACF's real return type is genuinely dynamic; the stubs correctly say mixed. The day-to-day pattern is a thin typed accessor per field type, written once:

/** @return array{url: string, alt: string, width: int, height: int}|null */
function theme_image(string $selector, int|string|null $post_id = null): ?array
{
    $value = get_field($selector, $post_id);
    return is_array($value) && isset($value['url']) ? $value : null;
}

/** @return list<array<string, mixed>> */
function theme_rows(string $selector, int|string|null $post_id = null): array
{
    $value = get_field($selector, $post_id);
    return is_array($value) ? $value : [];
}

Everything downstream (templates, view models, Blade/Twig data) consumes the wrappers, and PHPStan level 6+ stays clean without a single @phpstan-ignore.

Repeaters and flexible content. The stub signatures for have_rows() / the_row() / get_sub_field() / get_row_layout() / get_row_index() are accurate, so the canonical loop analyses fine:

if (have_rows('slides', $post_id)) {
    while (have_rows('slides', $post_id)) {
        the_row();
        $heading = (string) get_sub_field('heading');   // still mixed, cast or wrap
    }
}

while (have_rows('sections')) {
    the_row();
    match (get_row_layout()) {
        'text_block' => get_template_part('blocks/text'),
        'hero'       => get_template_part('blocks/hero'),
        default      => null,
    };
}

Options pages. get_field('logo', 'option') is typed mixed with $post_id accepting int|string|false|null. Wrap options reads the same way as post reads (theme_option('logo')) so the 'option' string is not scattered through templates.

Programmatic field groups. acf_add_local_field_group(array $field_group): void gives you existence checking but no shape checking. Pair it with an array-shape annotation so your IDE at least autocompletes the keys:

/** @var array{key: string, title: string, fields: list<array<string, mixed>>, location: list<list<array<string, mixed>>>} $group */
$group = [ /* ... */ ];
acf_add_local_field_group($group);

For anything larger than two groups, a small builder class returning that shape is worth more than the stubs alone.

ACF Blocks. acf_register_block_type() is covered, so registration arrays validate on argument count and on the render_template/render_callback keys you actually pass. Register inside acf/init:

add_action('acf/init', static function (): void {
    acf_register_block_type([
        'name'            => 'hero',
        'title'           => __('Hero', 'theme'),
        'render_template' => 'blocks/hero.php',
        'category'        => 'formatting',
        'icon'            => 'cover-image',
        'mode'            => 'preview',
        'supports'        => ['align' => true, 'jsx' => true],
    ]);
});

CI wiring. Run the stubs against the whole theme, not just src/. Generate a baseline once (--generate-baseline) on a legacy codebase, then shrink it; new code gets zero tolerance. Cache .phpstan keyed on composer.lock so a stub bump invalidates it.

Gotchas and Tips

  • Duplicate symbol errors. If the ACF plugin directory is inside your analysed paths (common when WordPress lives in the repo), PHPStan sees two definitions of get_field and errors. Exclude wp-content/plugins/advanced-custom-fields-pro from paths/excludePaths — the stubs replace it.
  • scanFiles, not stubFiles. scanFiles registers the symbols; stubFiles overrides signatures and is the escape hatch if a stub is wrong. Confusing the two produces "function not found" or silent no-ops. The package ships no extension.neon, so phpstan/extension-installer will not pick it up automatically — the manual scanFiles entry is mandatory.
  • Stub coverage lags ACF PRO releases. The stub file is maintained separately from the plugin; new ACF 6.x functions can be missing and produce false "undefined function" errors even though the code runs. Check the stub file before blaming PHPStan, then add a tiny local stub of your own to scanFiles rather than sprinkling ignores.
  • mixed is correct, not a bug. Every get_field() return trips PHPStan level 6+. Fix it with wrapper functions (see above) or assert(is_string($x)), not with a global mixed tolerance — the whole point of the stubs is that the other 90% is checked.
  • No field-name validation. The stubs cannot know that 'hero_titel' is a typo; field keys, names and types are runtime data. Consider a constants class or enum for field names if string-typo bugs are recurring.
  • IDE vs. analyser. In PhpStorm, adding the stub file as an Include Path gives autocompletion but shows "multiple definitions" when the plugin is also in the project. Prefer relying on PHPStan for correctness and only add the stub via Settings → PHP → Include Path when the plugin is genuinely absent from the project.
  • Dev-only, always. Install with --dev and deploy with composer install --no-dev. Committing the stubs into the repo is a smell; committing a phpstan.neon that references vendor/... is correct.
  • Extension point. Both PHPStan and Psalm accept multiple files. Keep your own stubs/acf-helpers.stub.php (generic helpers, project-specific acf_* wrappers, missing functions) in scanFiles next to the vendor file, so upgrading the package never overwrites your additions.
  • Hook callbacks are unchecked. acf/init, acf/save_post, acf/validate_value are WP actions; the stubs describe ACF's functions but not hook signatures, so callback argument types are still on you. Annotate the callback with @param and PHPStan will check the body.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin