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

File wp-object-oriented-foundations.diff, 21.0 KB (added by npetetin, 5 months 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' );