0

I'm trying to separate plugin structure files by using the Best Practice method on wordpress org Best Practice.

in plugins\plugin_name\public\index1.php there's the class for enqueue script

<?php


class Public_script
{


    function __construct()
    {
        // get current admin screen, or null
        $screen = get_current_screen();

        // verify admin screen object

        // enqueue only for specific post types

        // enqueue script
        wp_enqueue_script('wporg_meta_box_script', plugin_dir_url(__FILE__) . 'public/js/main.js', ['jquery']);

        // localize script, create a custom js object
        wp_localize_script(
            'wporg_meta_box_script',
            'wporg_meta_box_obj',
            [
                'url' => admin_url('admin-ajax.php'),
            ]
        );
        wp_localize_script(
            'wporg_meta_box_script',
            'ajax_object',
            array('ajax_url' => admin_url('admin-ajax.php'), 'we_value' => 1234)
        );

        wp_register_style('wpdocsPluginStylesheet', plugins_url('public/css/style.css', __FILE__));
        wp_enqueue_style('wpdocsPluginStylesheet');
    }
}

on the main dir of the plugin plugins\plugin_name\index.php

I'm tring to execute this action (Also trued with public static function) it prints this error Fatal error: Uncaught Error: Class "Public_script" not found in it's working normally if every function in one file

add_action('admin_enqueue_scripts', $js_scripts = new Public_script());
10
  • using classes isn't what the linked document recommends, it suggests them as a way to avoid name collisions, PHP namespaces would work just as well. In this situation classes are unnecessary. Did you include your files with your class before trying to use the class in code? It's highly unusual to do work in the constructor of a class.
    – Tom J Nowell
    Commented Feb 13, 2023 at 16:25
  • Yes I did include the class file here ` define("Plugin_root", plugin_dir_url(FILE)); include_once(Plugin_root . 'public/index1.php');` I tried changing the constuctor to public function and public static function didn't work either Commented Feb 13, 2023 at 17:01
  • As well as, OOP is pretty recommended as best practice and in type of the plugin thatI create. Commented Feb 13, 2023 at 17:02
  • it's a constructor, it will always be public, and it can't be static, but you can't add functions from a class into an add_action call like that, it has to be a value that's callable and PHP has a strict set of examples of what's allowed. Also the example you used at the end would immediately create the object and run the code inside. You're also incorrect, what you've done is not OOP, unless you intend to create multiple instances of that class with internal state it's just a bunch of procedural code inside a class being used for namespacing
    – Tom J Nowell
    Commented Feb 13, 2023 at 17:07
  • keep in mind that when that document was written, WordPress required PHP 5.2 as a minimum which did not support the namespace keyword which was added in v5.3, classes and function name prefixes were the workaround, hence the recommendation, it's not a general programming recommendation. Even then, you're doing work in an objects constructor which is very bad, work needs to be done in methods, not the constructor
    – Tom J Nowell
    Commented Feb 13, 2023 at 17:08

1 Answer 1

0

There are several problems here:

Problem 1: plugin_dir_url

In a comment you mentioned using this code to load the file the class is in:

define("Plugin_root", plugin_dir_url(FILE));
include_once(Plugin_root . 'public/index1.php');

This results in this value being used in include_once:

include_once( 'http://example.com/wp-content/plugin/public/index1.php` );

Which is why you're getting PHP fatal errors when you try to use the class. It's very likely if you try to look at your PHP error log you'll see messages like this:

PHP Warning include_once Failed opening '....' for inclusion

Instead use plugin_dir_path when using require or include.

Or better yet, make it work anywhere with:

require_once __DIR_ . '/public/index1.php';

Since this class needs to be included, why not use require_once instead?

Problem 2: Code is in the Constructor

Code in the constructor runs the moment the object is created, so if you do this:

add_action('admin_enqueue_scripts', $js_scripts = new Public_script());

It's the same thing as this:

$js_scripts = new Public_script()
add_action('admin_enqueue_scripts', $js_scripts );

Which is far too early, and results in a broken add_action instead do work in a class method/function.

$js_scripts = new Public_script();
add_action('admin_enqueue_scripts', [ $js_scripts, 'your_new_do_enqueing_function' ] );

Constructors are for receiving initial variables and doing any initial setup the object needs to be able to function as an object. Aka dependency injection and initial values. You shouldn't do work as it makes the object very difficult to write unit tests for, reduces performance, and can cause problems like this one.

Even better, doing it in a more OOP way you would call add_action inside the class, or in an init function:

public function run() : void {
    add_action( 'admin_enqueue_scripts', [ $this, 'your_new_do_enqueing_function' ] );
}
$js_scripts = new Public_script()
$js_scripts->run();

The run function can then call all of the add_action and add_filter calls. This is similar to the boilerplate you referenced.

You might see some people call add_action in the constructor, and while this works, it's not ideal as there's no way to create the object without also hooking everything in, and no way to let the object do stuff before that happens, e.g. if you wanted to check something first or enable a flag. It would all have to be done in the constructor, and you'd need to know everything before you create the object which makes things difficult if you need information that's only available after a certain hook, e.g. the is_archive() or is_single type functions


Note that while classes and OOP are useful, nothing you've done here is actually object oriented programming, and there's been a misunderstanding of the WP documentation. WP isn't recommending using a class, it's recommending classes as a solution to PHP v5.2 not supporting namespaces.

So for this, you could just as easily use functions and it would use less memory, run a tiny bit faster, and be much easier to understand.

This is not to say that you should never use classes, they're a tool, and have a time and a place.

If you actually wanted to do something that was object oriented, you'd have built a class that you could create multiple objects with, that wraps commonly used steps to make life easier.

E.g. something used like this:

$a = new AdminAsset( 'js/admin.js' );
$b = new AdminAsset( 'js/admin.css' );
$a->enqueue();
$b->enqueue();

Now we have an actual object that has internal state, and wraps up all the code needed to attach to the admin enqueue hook, figure out the path and URL of the file, register and enqueue it, etc. This example isn't possible with plain functions, AdminAsset has internal state, and you can have more than one of them.

Likewise you could pass it additional parameters for inline assets, or create an extra function so you could so stuff like $a->add_inline_data( ... ), or functions such as is_enqueued, etc.

You shouldn't force everything into OO and use classes for everything, that's a common mistake! Use OOP and classes where it has its benefits and makes sense, there is such a thing as bad OOP, and bad classes ( e.g. look at the google clean code talks on singletons, a notorious anti-pattern that unfortunately is very popular in WP agencies and plugin code ). Also take a look at the difference between pure and impure functions, sometimes a function can be far superior at certain tasks to everything else if it's written in a particular way

I would also be sure to keep to WP Coding standards, so use a filename that contains the name of the class, use { on the same line not a newline, and add spaces inside parenthesis e.g. test( $var ) not test($var)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.