BrianHenryIE / strauss

Prefix PHP namespaces and classnames to allow multiple versions of libraries to exist without conflict.
https://brianhenryie.github.io/strauss/
MIT License
137 stars 21 forks source link

Error on call to undefined WP function #42

Closed kupoback closed 2 months ago

kupoback commented 2 years ago

So switching from Mozart to this, has been fantastic as this is all working out of the box without the need for patches, or figuring out which versions of a composer package I need. However, when I made a new class with an admin notice add_action, just to test and ensure this is all properly working, I get the following error. Am I missing something, do I need to somehow wrap the wp core as well? I know the way I'm calling to the action is fine, but not sure why this error would trigger when calling to a default WP function.

PHP Fatal error:  Uncaught Error: Call to undefined function add_action() in /home/user/Sites/functionplayground/web/app/plugins/rap-site-reset/rap-site-reset.php:34
Stack trace:
#0 /home/user/Sites/functionplayground/web/app/plugins/rap-site-reset/vendor/composer/autoload_real.php(78): require()
#1 /home/user/Sites/functionplayground/web/app/plugins/rap-site-reset/vendor/composer/autoload_real.php(61): composerRequire9a0dc73fcbf3506c717760c1fe787923()
#2 /home/user/Sites/functionplayground/web/app/plugins/rap-site-reset/vendor/autoload.php(7): ComposerAutoloaderInit9a0dc73fcbf3506c717760c1fe787923::getLoader()
#3 /home/user/Sites/functionplayground/web/app/plugins/rap-site-reset/vendor/brianhenryie/strauss/bin/strauss(5): require('/home/user/Site...')
#4 /home/user/Sites/functionplayground/web/app/plugins/rap-site-reset/vendor/brianhenryie/strauss/bin/strauss(28): {closure}()
#5 /home/user/Sites/functionplayground/web/app/plugins/rap-site-reset/vendor/bin/strauss(112): include('/home/user/Site...')
#6 {main}
  thrown in /home/user/Sites/functionplayground/web/app/plugins/rap-site-reset/rap-site-reset.php on line 34

Here's my composer.json

{
    "name": "kupoback/rap-site-reset",
    "description": "",
    "type": "wordpress-muplugin",
    "require": {
        "composer/installers": "^2.0",
        "ext-json": "*",
        "php": "^7.4|^8.0.0",
        "phpseclib/phpseclib": "dev-master",
        "wp-cli/db-command": "^2.0",
        "wp-cli/extension-command": "^2.0",
        "wp-cli/maintenance-mode-command": "2.x-dev",
        "illuminate/support": "^8.0"
    },
    "require-dev": {
        "squizlabs/php_codesniffer": "4.0.x-dev",
        "roave/security-advisories": "dev-master",
        "brianhenryie/strauss": "dev-master"
    },
    "config": {
        "optimize-autoloader": true,
        "preferred-install": "dist",
        "allow-plugins": {
            "composer/installers": true,
            "cweagans/composer-patches": true
        }
    },
    "license": "MIT",
    "scripts": {
        "strauss": [
            "vendor/bin/strauss"
        ],
        "post-root-package-install": [
            "@strauss"
        ],
        "post-root-package-update": [
            "@strauss"
        ],
        "test": [
            "phpcs"
        ]
    },
    "authors": [
        {
            "name": "kupoback",
        }
    ],
    "autoload": {
        "files": [ "rap-site-reset.php" ],
        "psr-4": {
            "RapReset\\": "src/"
        }
    },
    "minimum-stability": "dev",
    "extra": {
        "strauss": {
            "classmap_prefix": "RapReset",
            "constant_prefix": "RR_",
            "delete_vendor_files": false,
            "namespace_prefix": "RapReset\\Vendor",
            "target_directory": "vendor_prefix"
        }
    }
}

Here's my rap-site-reset.php file:

use RapReset\RapReset;

require_once __DIR__ . '/vendor_prefix/autoload.php';

add_action('admin_notices', [RapReset::class, 'adminNotice']);

And my Class

namespace RapReset;

class RapReset
{
    public static function adminNotice()
    {
        echo "Hello World";
    }
}
kupoback commented 2 years ago

Issue is with me, not the package. It's actually prolly a WP issue with how they do their functions and I might need to explore other options to call to those.

EDIT: There's gotta be something I'm missing or some setting from this that's not implemented. Would love help.

BrianHenryIE commented 2 years ago

Hey, sorry, I saw "Issue is with me, not the package." and never checked back.

What's happening is you're using an undefined function add_action in rap-site-reset.php, which you're autoloading in your files autoloader:

"autoload": {
  "files": [ "rap-site-reset.php" ],
  "psr-4": {
    "RapReset\\": "src/"
  }
},

The files autoloader is relatively dumb compared to classmap or PSR-4 which only load the file when the class is first needed, but files loads the files as soon as vendor/autoload.php is loaded.

When you use this inside a WordPress plugin, WordPress is already loaded, so obviously add_action is ready to use.

I'll take a look at how PhpStan WordPress and at how WP_Mock stub the functions. In principle, it's just a matter of:

if( ! function_exists( 'add_action' ) ) {
 function add_action( $hook_name, $callback, $priority = 10, $accepted_args = 1 ) { }
}

but I'll have to think about it. It should be safe to define those functions when Strauss is running, but maybe not when it's run as part of a Composer script (i.e. I don't think it has its own process).

That's pretty wild that you're prefixing WP CLI commands, that never crossed my mind.

BrianHenryIE commented 4 months ago

The correct solution is to start each file that uses WordPress functions with:

if( ! defined( 'ABSPATH' ) ) {
  return;
}

Otherwise, as packages that are autoloaded by Composer are autoloaded when running dev tools they will try to load WordPress functions which won't be present.