WordPress.org

Make WordPress Core

Ticket #22958: wp-object-oriented-foundations.diff

File wp-object-oriented-foundations.diff, 21.0 KB (added by npetetin, 8 years ago)

Patch for WordPress object-oriented foundations

  • wp-includes/class-wp-factory.php

     
     1<?php
     2/**
     3 * WP Factory class.
     4 *
     5 * @package WordPress
     6 * @since 3.X
     7 */
     8class WP_Factory {
     9       
     10        /**
     11         * Protected registered classes.
     12         *
     13         * List of registered classes.
     14         *
     15         * @since 3.X
     16         * @access protected
     17         * @var array
     18         */
     19        protected $registeredClasses;
     20       
     21        /**
     22         * Protected registered objects.
     23         *
     24         * List of registered objects.
     25         *
     26         * @since 3.X
     27         * @access protected
     28         * @var array
     29         */
     30        protected $registeredObjects;
     31       
     32        /**
     33         * Class constructor.
     34         *
     35         * Initializes the registered classes and objects lists.
     36         *
     37         * @since 3.X
     38         * @access public
     39         * @uses add_action() Adds private method to 'muplugins_loaded' action hook.
     40         * @uses add_action() Adds private method to 'plugins_loaded' action hook.
     41         */
     42        public function __construct() {
     43                $this->registeredClasses = array();
     44                $this->registeredObjects = array();
     45                add_action( 'muplugins_loaded', array( $this, '_muPluginsLoaded' ), PHP_INT_MAX  );
     46                add_action( 'plugins_loaded', array( $this, '_pluginsLoaded' ), PHP_INT_MAX  );
     47        }
     48       
     49        /**
     50         * Register class to the object factory.
     51         *
     52         * The class name and file path must be passed.
     53         * Additionally, the class may replace another registered class (already or not),
     54         * and the replaced class name is passed as third argument.
     55         * Finally, the class may require another registered class (already or not),
     56         * and the required class name is passed as forth argument.
     57         *
     58         * @since 3.X
     59         * @access public
     60         *
     61         * @param string $name The class name.
     62         * @param string $file The absolute path of the class file.
     63         * @param string $replaces Optional. The name of the class to be replaced.
     64         * @param string $requires Optional. The name of the required class (most often the parent class).
     65         * @param string $extends Optional. The name of the class that must be extended.
     66         * @return true|WP_Error True in case of success and WP_Error in case of failure.
     67         */
     68        public function registerClass( $name, $file, $replaces = false, $requires = false, $extends = false ) {
     69                $result = true;
     70                if ( file_exists( $file ) ) {
     71                        $this->registeredClasses[ $name ] = ( object ) array( 'file' => $file, 'substitute' => isset( $this->registeredClasses[ $name ] ) ? $this->registeredClasses[ $name ]->substitute : false, 'requires' => $requires, 'extends' => $extends );
     72                        if ( $replaces ) {
     73                                if ( isset( $this->registeredClasses[ $replaces ] ) ) {
     74                                        $this->registeredClasses[ $replaces ]->substitute = $name;
     75                                } else {
     76                                        $this->registeredClasses[ $replaces ] = ( object ) array( 'file' => false, 'substitute' => $name, 'requires' => false, 'extends' => false );
     77                                }
     78                        }
     79                } else {
     80                        $result = new WP_Error( 'wp_factory_error', printf( __( 'Class file %s does not exist.' ), $file ) );
     81                }
     82                return $result;
     83        }
     84       
     85        /**
     86         * Register object to the object factory.
     87         *
     88         * The unique name and the class of the object must be passed.
     89         * The class of the object must be previously registered.
     90         * Additionally, a single argument may be passed for construction of the object.
     91         * In case of multiple arguments required, an array must be used in the constructor.
     92         * Finally, an optional flag orders to instantiate the object on 'plugins_loaded' action.
     93         *
     94         * @since 3.X
     95         * @access public
     96         *
     97         * @param string $name The object name.
     98         * @param string $class The registered object class.
     99         * @param mixed $args Optional. The construction arguments.
     100         * @param bool $init Optional. The flag to instantiate the object on 'plugins_loaded' action.
     101         * @return true|WP_Error True in case of success and a WP_Error in case of failure.
     102         */
     103        public function registerObject( $name, $class, $args = null, $init = false ) {
     104                $result = true;
     105                if ( isset( $this->registeredClasses[ $class ] ) ) {
     106                        $this->registeredObjects[ $name ] = ( object ) array( 'class' => $class, 'args' => $args, 'object' => $init );
     107                } else {
     108                        $result = new WP_Error( 'wp_factory_error', printf( __( 'Class %s is not registered.' ), $class ) );
     109                }
     110                return $result;
     111        }
     112       
     113        /**
     114         * Return the registration settings of the class.
     115         *
     116         * If the class is previously registered, the returned object has the following attributes:
     117         * 'file': its class path
     118         * 'substitute': the actual class that is used on object construction
     119         * 'requires': the name of its required class
     120         *
     121         * @since 3.X
     122         * @access public
     123         *
     124         * @param string $name The class name.
     125         * @return object|null The registration settings object or null if not registered.
     126         */
     127        public function getRegisteredClass( $name ) {
     128                return isset( $this->registeredClasses[ $name ] ) ? clone( $this->registeredClasses[ $name ] ) : null;
     129        }
     130       
     131        /**
     132         * Return the registration settings of the named object.
     133         *
     134         * If the object is previously registered with the same name, the returned object has the following attributes:
     135         * 'class': the class name it has been registered with
     136         * 'args': the array of arguments to be passed on construction
     137         * 'object': the object itself if already instanciated or the $init argument value it has been registered with
     138         *
     139         * @since 3.X
     140         * @access public
     141         *
     142         * @param string $name The object name.
     143         * @return object|null The registration settings object or null if not registered.
     144         */
     145        public function getRegisteredObject( $name ) {
     146                return isset( $this->registeredObjects[ $name ] ) ? clone( $this->registeredObjects[ $name ] ) : null;
     147        }
     148       
     149        /**
     150         * Return the named object.
     151         *
     152         * The object must be previously registered.
     153         * Implements the singleton design pattern.
     154         *
     155         * @since 1.0
     156         * @access public
     157         * @uses self::newObject() Instantiate the singleton object
     158         *
     159         * @param string $name The object name.
     160         * @return object|WP_Error The singleton in case of success and a WP_Error in case of failure.
     161         */
     162        public function getObject( $name ) {
     163                $result = null;
     164                if ( isset( $this->registeredObjects[ $name ] ) ) {
     165                        $data = $this->registeredObjects[ $name ];
     166                        if ( !is_object( $data->object ) || is_wp_error( $data->object ) ) {
     167                                $data->object = $this->newObject( $data->class, $data->args );
     168                        }
     169                        $result = $data->object;
     170                } else {
     171                        $result = new WP_Error( 'wp_factory_error', printf( __( 'Object %s is not registered.' ), $name ) );
     172                }
     173                return $result;
     174        }
     175       
     176        /**
     177         * Instantiate a polymorphic object.
     178         *
     179         * The class must be previously registered.
     180         * If an additional class has been registered to replace the original class,
     181         * the replacing class is instantiated instead.
     182         * The replacing class must inherit from the original class.
     183         * The optional construction argument is single and must be the same way in the class's constructor.
     184         * In case of multiple arguments required, an array must be used in the constructor.
     185         * Includes all required class files.
     186         *
     187         * @since 3.X
     188         * @access public
     189         *
     190         * @param string $class The object class name.
     191         * @param mixed $args Optional. The construction arguments.
     192         * @return object|WP_Error The desired object in case of success and a WP_Error in case of failure.
     193         */
     194        public function newObject( $class, $args = null ) {
     195                $result = false;
     196                if ( isset( $this->registeredClasses[ $class ] ) ) {
     197                        $includes = array();
     198                        $this->addInclude( &$includes, $class );
     199                        foreach ( $includes as $include ) {
     200                                try {
     201                                        require_once( $include );
     202                                } catch ( Exception $e ) {
     203                                        $result = new WP_Error( 'wp_factory_error', $e->getMessage() );
     204                                        break;
     205                                }
     206                        }
     207                        if ( !is_wp_error( $result ) ) {
     208                                $base_class = $this->registeredClasses[ $class ]->extends ? $this->registeredClasses[ $class ]->extends : $class;
     209                                if ( ( $base_class != $class ) && !is_subclass_of( $class, $base_class ) ) {
     210                                        $result = new WP_Error( 'wp_factory_error', printf( __( 'Class %s does not extend %s.' ), $class, $base_class ) );
     211                                }
     212                        }
     213                        if ( !is_wp_error( $result ) ) {
     214                                $new_class = $this->registeredClasses[ $class ]->substitute ? $this->registeredClasses[ $class ]->substitute : $class;
     215                                if ( is_subclass_of( $new_class, $class ) ) {
     216                                        try {
     217                                                $result = new $new_class( $args );
     218                                        } catch ( Exception $e ) {
     219                                                $result = new WP_Error( 'wp_factory_error', $e->getMessage() );
     220                                        }
     221                                } else {
     222                                        $result = new WP_Error( 'wp_factory_error', printf( __( 'Class %s does not extend %s.' ), $new_class, $class ) );
     223                                }
     224                        }
     225                } else {
     226                        $result = new WP_Error( 'wp_factory_error', printf( __( 'Class %s is not registered.' ), $class ) );
     227                }
     228                return $result;
     229        }
     230       
     231        /**
     232         * Dispose an object from filters and action hooks and other dependencies
     233         * so it's no longer referenced by any current process.
     234         *
     235         * The object may or may not be created by the factory.
     236         *
     237         * @since 3.X
     238         * @access public
     239         * @uses remove_filter() Remove all filters and action hooks
     240         *
     241         * @param object &$object The object to be disposed.
     242         */
     243        public function disposeObject( &$object ) {
     244                // Integrated from wp-includes/plugin.php::remove_filter()
     245                $functions_to_remove = array();
     246                $tags = $GLOBALS[ 'wp_filter' ];
     247                foreach ( $tags as $tag => $priorities ) {
     248                        foreach ( $priorities as $priority => $functions ) {
     249                                foreach ( $functions as $function_idx => $function ) {
     250                                        if ( is_array( $function[ 'function' ] ) && ( $object == $function[ 'function' ][ 0 ] ) ) {
     251                                                $functions_to_remove[] = array( $tag, $function[ 'function' ][ 1 ], $priority, $function[ 'accepted_args' ] );
     252                                        }
     253                                }
     254                        }
     255                }
     256                foreach ( $functions_to_remove as $function ) {
     257                        remove_filter( $function[ 0 ], array( &$object, $function[ 1 ] ), $function[ 2 ], $function[ 3 ] );
     258                }
     259        }
     260       
     261        /**
     262         * Add include file to includes list.
     263         *
     264         * Recursive function.
     265         *
     266         * @since 3.X
     267         * @access protected
     268         *
     269         * @param array &$includes The list of includes.
     270         * @param string $class The class name.
     271         */
     272        protected function addInclude( &$includes, $class ) {
     273                if ( isset( $this->registeredClasses[ $class ] ) ) {
     274                        // 1. Include required classes first
     275                        if ( $this->registeredClasses[ $class ]->extends ) {
     276                                $this->addInclude( &$includes, $this->registeredClasses[ $class ]->extends );
     277                        }
     278                        if ( $this->registeredClasses[ $class ]->requires ) {
     279                                $this->addInclude( &$includes, $this->registeredClasses[ $class ]->requires );
     280                        }
     281                        // 2. Include current class
     282                        if ( !in_array( $this->registeredClasses[ $class ]->file, $includes ) ) {
     283                                $includes[] = $this->registeredClasses[ $class ]->file;
     284                        }
     285                        // 3. Include subsitute class at last
     286                        if ( $this->registeredClasses[ $class ]->substitute ) {
     287                                $this->addInclude( &$includes, $this->registeredClasses[ $class ]->substitute );
     288                        }
     289                }
     290        }
     291       
     292        /**
     293         * Instantiate objects flagged as such.
     294         *
     295         * @since 3.X
     296         * @access protected
     297         *
     298         * @param bool|string $init The instantiation flag.
     299         */
     300        protected function initObjects( $init ) {
     301                foreach ( $this->registeredObjects as $name => $data ) {
     302                        if ( $data->object === $init ) {
     303                                $data->object = $this->newObject( $data->class, $data->args );
     304                        }
     305                }
     306        }
     307       
     308        /**
     309         * 'muplugins_loaded' action hook.
     310         *
     311         * @since 3.X
     312         * @access private
     313         * @uses self::initObjects() 'muplugins' instantiation flag
     314         */
     315        function _muPluginsLoaded() {
     316                $this->initObjects( 'muplugins' );
     317        }
     318       
     319        /**
     320         * 'plugins_loaded' action hook.
     321         *
     322         * @since 3.X
     323         * @access private
     324         * @uses self::initObjects() True instantiation flag
     325         */
     326        function _pluginsLoaded() {
     327                $this->initObjects( true );
     328        }
     329       
     330}
  • wp-includes/factory.php

     
     1<?php
     2/**
     3 * WordPress Core API for object factory
     4 *
     5 * @package WordPress
     6 * @since 3.X
     7 */
     8
     9/**
     10 * Register class to the object factory.
     11 *
     12 * This function is a proxy interface of the 'wp_factory' object's method.
     13 * The class name and file path must be passed.
     14 * Additionally, the class may replace another registered class (already or not),
     15 * and the replaced class name is passed as third argument.
     16 * Finally, the class may require another registered class (already or not),
     17 * and the required class name is passed as forth argument.
     18 *
     19 * @since 3.X
     20 * @uses WP_Factory::registerClass() Delegate to the 'wp_factory' object.
     21 * @uses do_action() Calls 'wp_class_registered' once the class is registered.
     22 *      Same arguments are passed.
     23 *
     24 * @param string $name The class name.
     25 * @param string $file The absolute path of the class file.
     26 * @param string $replaces Optional. The name of the class to be replaced.
     27 * @param string $requires Optional. The name of the required class (most often the parent class).
     28 * @param string $extends Optional. The name of the class that must be extended.
     29 * @return bool|WP_Error True in case of success and WP_Error in case of failure.
     30 */
     31function wp_register_class( $name, $file, $replaces = false, $requires = false, $extends = false ) {
     32        $factory = wp_get_object( 'wp_factory' );
     33        $result = true;
     34        if ( is_a( $factory, 'WP_Factory' ) ) {
     35                $result = $factory->registerClass( $name, $file, $replaces, $requires, $extends );
     36        } else {
     37                $result = new WP_Error( 'wp_factory_error', __( 'Factory is not a WP_Factory instance.' ) );
     38        }
     39        if ( $result && !is_wp_error( $result ) ) {
     40                do_action( 'wp_class_registered', $name, $file, $replaces, $requires, $extends );
     41        }
     42        return $result;
     43}
     44
     45/**
     46 * Register object to the object factory.
     47 *
     48 * This function is a proxy interface of the 'wp_factory' object's method.
     49 * The unique name and the class of the object must be passed.
     50 * The class of the object must be previously registered.
     51 * Additionally, a single argument may be passed for construction of the object.
     52 * In case of multiple arguments required, an array must be used in the constructor.
     53 * Finally, an optional flag orders to instantiate the object on 'plugins_loaded' action.
     54 *
     55 * @since 3.X
     56 * @uses WP_Factory::registerObject() Delegate to the 'wp_factory' object.
     57 * @uses do_action() Calls 'wp_object_registered' once the object is registered.
     58 *      Same arguments are passed.
     59 *
     60 * @param string $name The object name.
     61 * @param string $class The registered object class.
     62 * @param mixed $args Optional. The construction arguments.
     63 * @param bool $init Optional. The flag to instantiate the object on 'plugins_loaded' action.
     64 * @return bool|WP_Error True in case of success and a WP_Error in case of failure.
     65 */
     66function wp_register_object( $name, $class, $args = null, $init = false ) {
     67        $factory = wp_get_object( 'wp_factory' );
     68        $result = true;
     69        if ( is_a( $factory, 'WP_Factory' ) ) {
     70                $result = $factory->registerObject( $name, $class, $args, $init );
     71        } else {
     72                $result = new WP_Error( 'wp_factory_error', __( 'Factory is not a WP_Factory instance.' ) );
     73        }
     74        if ( $result && !is_wp_error( $result ) ) {
     75                do_action( 'wp_object_registered', $name, $class, $args, $init );
     76        }
     77        return $result;
     78}
     79
     80/**
     81 * Return the registration settings of the class.
     82 *
     83 * If the class is previously registered, the returned object has the following attributes:
     84 * 'file': its class path
     85 * 'substitute': the actual class that is used on object construction
     86 * 'requires': the name of its required class
     87 *
     88 * @since 3.X
     89 * @uses WP_Factory::getRegisteredClass() Delegate to the 'wp_factory' object.
     90 * @uses apply_filters() Applies 'wp_registered_class' on the returned object.
     91 *
     92 * @param string $name The class name.
     93 * @return object|null|WP_Error The registration settings object or null if not registered or WP_Error in case of error.
     94 */
     95function wp_get_registered_class( $name ) {
     96        $factory = wp_get_object( 'wp_factory' );
     97        $result = null;
     98        if ( is_a( $factory, 'WP_Factory' ) ) {
     99                $result = $factory->getRegisteredClass( $name );
     100        } else {
     101                $result = new WP_Error( 'wp_factory_error', __( 'Factory is not a WP_Factory instance.' ) );
     102        }
     103        if ( !is_wp_error( $result ) ) {
     104                $result = apply_filters( 'wp_registered_class', $result, $name );
     105        }
     106        return $result;
     107}
     108
     109/**
     110 * Return the registration settings of the named object.
     111 *
     112 * If the object is previously registered with the same name, the returned object has the following attributes:
     113 * 'class': the class name it has been registered with
     114 * 'args': the array of arguments to be passed on construction
     115 * 'object': the object itself if already instanciated or the $init argument value it has been registered with
     116 *
     117 * @since 3.X
     118 * @uses WP_Factory::getRegisteredObject() Delegate to the 'wp_factory' object.
     119 * @uses apply_filters() Applies 'wp_registered_object' on the returned object.
     120 *
     121 * @param string $name The object name.
     122 * @return object|null|WP_Error The registration settings object or null if not registered or WP_Error in case of error.
     123 */
     124function wp_get_registered_object( $name ) {
     125        $factory = wp_get_object( 'wp_factory' );
     126        $result = null;
     127        if ( is_a( $factory, 'WP_Factory' ) ) {
     128                $result = $factory->getRegisteredObject( $name );
     129        } else {
     130                $result = new WP_Error( 'wp_factory_error', __( 'Factory is not a WP_Factory instance.' ) );
     131        }
     132        if ( !is_wp_error( $result ) ) {
     133                $result = apply_filters( 'wp_registered_object', $result, $name );
     134        }
     135        return $result;
     136}
     137
     138/**
     139 * Return the named object.
     140 *
     141 * This function is a proxy interface of the 'wp_factory' object's method.
     142 * The object must be previously registered.
     143 * Implements the singleton design pattern.
     144 *
     145 * @since 3.X
     146 * @uses WP_Factory::getObject() Delegate to the 'wp_factory' object.
     147 *
     148 * @param string $name The object name.
     149 * @return object|WP_Error The singleton in case of success and a WP_Error in case of failure.
     150 */
     151function wp_get_object( $name ) {
     152        global $wp_factory;
     153        if ( is_null( $wp_factory ) ) {
     154                require_once( path_join( ABSPATH . WPINC, 'class-wp-factory.php' ) );
     155                $wp_factory = new WP_Factory();
     156        }
     157        $result = null;
     158        if ( $name == 'wp_factory' ) {
     159                $result = $wp_factory;
     160        } else {
     161                $result = $wp_factory->getObject( $name );
     162        }
     163        return $result;
     164}
     165
     166/**
     167 * Instantiate a polymorphic object.
     168 *
     169 * This function is a proxy interface of the 'wp_factory' object's method.
     170 * The class must be previously registered.
     171 * If an additional class has been registered to replace the original class,
     172 * the replacing class is instantiated instead.
     173 * The replacing class must inherit from the original class.
     174 * The optional construction argument is single and must be the same way in the class's constructor.
     175 * In case of multiple arguments required, an array must be used in the constructor.
     176 * Includes all required class files.
     177 *
     178 * @since 3.X
     179 * @uses WP_Factory::newObject() Delegate to the 'wp_factory' object.
     180 *
     181 * @param string $class The object class name.
     182 * @param mixed $args Optional. The construction arguments.
     183 * @return object|WP_Error The desired object in case of success and a WP_Error in case of failure.
     184 */
     185function wp_new_object( $class, $args = null ) {
     186        $factory = wp_get_object( 'wp_factory' );
     187        $result = true;
     188        if ( is_a( $factory, 'WP_Factory' ) ) {
     189                $result = $factory->newObject( $class, $args );
     190        } else {
     191                $result = new WP_Error( 'wp_factory_error', __( 'Factory is not a WP_Factory instance.' ) );
     192        }
     193        return $result;
     194}
     195
     196/**
     197 * Dispose an object from filters and action hooks and other dependencies
     198 * so it's no longer referenced by any current process.
     199 *
     200 * The object may or may not be created by the factory.
     201 *
     202 * @since 3.X
     203 * @uses WP_Factory::disposeObject() Delegate to the 'wp_factory' object.
     204 *
     205 * @param object &$object The object to be disposed.
     206 * @return bool|WP_Error True in case of success and a WP_Error in case of failure.
     207 */
     208function wp_dispose_object( &$object ) {
     209        $factory = wp_get_object( 'wp_factory' );
     210        $result = true;
     211        if ( is_a( $factory, 'WP_Factory' ) ) {
     212                $factory->disposeObject( &$object );
     213        } else {
     214                $result = new WP_Error( 'wp_factory_error', __( 'Factory is not a WP_Factory instance.' ) );
     215        }
     216        return $result;
     217}
  • wp-settings.php

     
    6363// Load early WordPress files.
    6464require( ABSPATH . WPINC . '/compat.php' );
    6565require( ABSPATH . WPINC . '/functions.php' );
     66require( ABSPATH . WPINC . '/factory.php' );
    6667require( ABSPATH . WPINC . '/class-wp.php' );
    6768require( ABSPATH . WPINC . '/class-wp-error.php' );
    6869require( ABSPATH . WPINC . '/plugin.php' );