[wp-trac] [WordPress Trac] #36335: Next generation: core autoloader proposal

WordPress Trac noreply at wordpress.org
Fri Mar 25 19:52:45 UTC 2016


#36335: Next generation: core autoloader proposal
-----------------------------+------------------------------
 Reporter:  dnaber-de        |       Owner:
     Type:  feature request  |      Status:  new
 Priority:  normal           |   Milestone:  Awaiting Review
Component:  General          |     Version:
 Severity:  normal           |  Resolution:
 Keywords:                   |     Focuses:
-----------------------------+------------------------------

Old description:

> Hello WordPress community. With this ticket I would like to get the
> debate of the last days about how to attract WordPress to developers (or
> the other way around?) to a concrete discussion on how a class autoloader
> could look like in WordPress core.
>
> So when we start to think about some major improvements like in
> https://core.trac.wordpress.org/ticket/36292, whe should also take some
> time and talking about autoloading.
>
> == Abstract ==
>
> A class autoloader is a basic tool to separate code writing form code
> organization. It takes care about providing class declaration at the
> point they are needed. The fact that WordPress lacks of a core autoloader
> was one point mentioned in the debate on what developers
> [https://www.alainschlesser.com/attracting-developers-wordpress/ missing
> most with WordPress].
>
> == Why we need an autloader ==
>
> Plugin authors using autoloaders these days. They even use composer for
> dependency management and ship their plugins with this dependencies. This
> practice [https://github.com/composer/composer/issues/3852 leads to
> trouble right now]. I'm convinced that in a long-range plan we even have
> to talk about how to deal with proper dependency management to overcome
> collisions. Having an autoloader in core is a precondition for this.
>
> == How an implementation could look like ==
>
> The following proposal follows a concept of separating the file locating
> and file loading process to avoid a violation of the single
> responsibility principle and to gain flexibility. All classes and
> interfaces are prefixed with `WP_Autoload_` to apply a pseudo namespace.
>
> The main instance of this concept is the interface `WP_Autolaod_Rule`. A
> autoload rule is against the client responsible for locating and loading
> a given class. The class is provided by its full qualified name. This
> leads to this interface signature:
>
> {{{#!php
> <?php
> interface WP_Autoload_Rule {
>
>     /**
>      * @param string $class (A full qualified class name)
>      *
>      * @return bool
>      */
>     public function load_class( $class );
> }
> }}}
>
> Implementations could be `WP_Autoload_Psr4Rule`, `WP_Autoload_Psr0Rule`
> or `WP_Autoload_ClassMapRule` or what ever plugin and theme authors want
> to implement for their requirements. Here's a quick example:
>
> {{{#!php
> <?php
> class WP_Autoload_Psr4Rule implements WP_Autoload_Rule {
>
>     /**
>      * @var string
>      */
>     private $base_namespace;
>
>     /**
>      * @var string
>      */
>     private $base_directory;
>
>     /**
>      * @var WP_Autoload_FileLoader
>      */
>     private $file_loader;
>
>     /**
>      * @param $base_namespace
>      * @param $base_directory
>      */
>     public function __construct( $base_namespace, $base_directory,
> $file_loader = NULL ) {
>
>         $this->base_directory = (string) $base_directory;
>         $this->base_namespace = (string) $base_namespace;
>         $this->file_loader    = $file_loader && is_a( $file_loader,
> 'WP_Autoload_Fileloader' )
>             ? $file_loader
>             : new WP_Autoload_IsReadableFileLoader;
>     }
>
>     /**
>      * @param string $class (A full qualified class name)
>      *
>      * @return bool
>      */
>     public function load_class( $class ) {
>
>         // performing the psr4 mapping here to get a $file
>         $file = '/whatever';
>
>         return $this->file_loader->load_file( $file );
>     }
>
> }
> }}}
>
> Autoloading rules should depend on a file loader. The file loader
> receives a file name and loads it, if it is present.
>
> {{{#!php
> <?php
> interface WP_Autoload_FileLoader {
>
>     /**
>      * @param string $file
>      *
>      * @return bool
>      */
>     public function load_file( $file );
> }
> }}}
>
> A very simple implementation can just ask for `is_readable()`. Looking on
> performance, another implementation could `glob()`-ing a given directory
> once to read in all PHP-files and matching the given file against this
> list. Even persistent caches are thinkable.
>
> Last but not least `WP_Autoload_Autoload` acts as a repository for all
> possible rules:
>
> {{{#!php
> <?php
> interface WP_Autoload_Autoload {
>
>     /**
>      * @param WP_Autoload_Rule $autoload_rule
>      *
>      * @return void
>      */
>     public function add_rule( $autoload_rule );
> }
> }}}
>
> The main implementation should be `WP_Autoload_AutoloadSpl` which
> connects to PHP standard library autoload:
>
> {{{#!php
> <?php
> class WP_Autoload_SplAutoload implements WP_Autoload_Autoload {
>
>     /**
>      * @var WP_Autoload_Rule[]
>      */
>     private $rules = array();
>
>     /**
>      * @param WP_Autoload_Rule $autoload_rule
>      *
>      * @return void
>      */
>     public function add_rule( $autoload_rule ) {
>
>         if ( ! in_array( $autoload_rule, $this->rules, TRUE ) )
>             $this->rules[] = $autoload_rule;
>     }
>
>     /**
>      * @param string $fqcn (full qualified class name)
>      */
>     public function load_class( $fqcn ) {
>
>         foreach ( $this->rules as $rule ) {
>             if ( $rule->load_class( $fqcn ) )
>                 break;
>         }
>     }
> }
> }}}
>
> == Instantiate and propagate the autoloader ==
>
> {{{#!php
> <?php
> function wp_setup_autoloader() {
>
>     $autoloader = new WP_Autoload_SplAutoload;
>     spl_autoload_register( array( $autoloader, 'load_class' ) );
>
>     /**
>      * Use the WordPress core autoloader to
>      * bootstrap your plugin and theme
>      *
>      * @param WP_Autoload_Autoload $autoloader
>      */
>     do_action( 'wp_autoload', $autoloader );
> }
> }}}
>
> `wp_setup_autoloader()` should then be called early in the WP loading
> process.
>
> {{{#!php
> <?php
> add_action( 'wp_autoload', function( $autoloader ) {
>
>     $autoloader->addRule(
>         new WP_Autoload_Psr4Rule(
>             'MyPlugin\\'
>             __DIR__ . '/src'
>         )
>     );
> } );
> }}}
>
> == Things to discuss ==
>
>  * The proposal uses [http://verraes.net/2013/09/sensible-interfaces/
> sensible interface names] which I prefer over naming interfaces like
> `WhateverInterfaces`. As WordPress does not provide interfaces right now,
> this is just a suggestion.
>  * PHP class identifiers are case insensitive. That means `new MySql` and
> `new MySQL` will both work, if a class `mysql` is declared. The
> autoloader should respect this. Now, Psr4 is very wide-spread and
> encourage developers to use case sensitive autoloaders and [https://r.je
> /php-autoloaders-should-not-be-case-sensitive.html this is a problem].
> How can we deal with this in a performant way?
>  * How should the WordPress core files be organized to work with the
> autoloader? Is it realistic to rearrange them, if not how could a
> corresponding `WP_Autoload_Rule` look like?
>  * What about compatibility with PHP 5.2.? The proposal uses
> `spl_autoload_register`. But before PHP 5.3.0 it is theoretically
> possible to deactivate the spl extension. In this case another
> implementation of `WP_Autolad_Autoload` would be necessary and maybe some
> adaptions to the other interfaces. But is this really the intention?
>
> == Finally ==
> Thanks for reading. Feel free to add your concerns, your opinions or even
> if I'm on a completely wrong train. In fact
> I'm really interested in critic. To be clear, I don't want to push this
> proposal but I would like to see a proper
> autoloader in core some day :)

New description:

 Hello WordPress community. With this ticket I would like to get the debate
 of the last days about how to attract WordPress to developers (or the
 other way around?) to a concrete discussion on how a class autoloader
 could look like in WordPress core.

 So when we start to think about some major improvements like in #36292, we
 should also take some time and talking about autoloading.

 == Abstract ==

 A class autoloader is a basic tool to separate code writing form code
 organization. It takes care about providing class declaration at the point
 they are needed. The fact that WordPress lacks of a core autoloader was
 one point mentioned in the debate on what developers
 [https://www.alainschlesser.com/attracting-developers-wordpress/ missing
 most with WordPress].

 == Why we need an autoloader ==

 Plugin authors using autoloaders these days. They even use composer for
 dependency management and ship their plugins with this dependencies. This
 practice [https://github.com/composer/composer/issues/3852 leads to
 trouble right now]. I'm convinced that in a long-range plan we even have
 to talk about how to deal with proper dependency management to overcome
 collisions. Having an autoloader in core is a precondition for this.

 == How an implementation could look like ==

 The following proposal follows a concept of separating the file locating
 and file loading process to avoid a violation of the single responsibility
 principle and to gain flexibility. All classes and interfaces are prefixed
 with `WP_Autoload_` to apply a pseudo namespace.

 The main instance of this concept is the interface `WP_Autoload_Rule`. An
 autoload rule is against the client responsible for locating and loading a
 given class. The class is provided by its full qualified name. This leads
 to this interface signature:

 {{{#!php
 <?php
 interface WP_Autoload_Rule {

     /**
      * @param string $class (A full qualified class name)
      *
      * @return bool
      */
     public function load_class( $class );
 }
 }}}

 Implementations could be `WP_Autoload_Psr4Rule`, `WP_Autoload_Psr0Rule` or
 `WP_Autoload_ClassMapRule` or what ever plugin and theme authors want to
 implement for their requirements. Here's a quick example:

 {{{#!php
 <?php
 class WP_Autoload_Psr4Rule implements WP_Autoload_Rule {

     /**
      * @var string
      */
     private $base_namespace;

     /**
      * @var string
      */
     private $base_directory;

     /**
      * @var WP_Autoload_FileLoader
      */
     private $file_loader;

     /**
      * @param $base_namespace
      * @param $base_directory
      */
     public function __construct( $base_namespace, $base_directory,
 $file_loader = NULL ) {

         $this->base_directory = (string) $base_directory;
         $this->base_namespace = (string) $base_namespace;
         $this->file_loader    = $file_loader && is_a( $file_loader,
 'WP_Autoload_Fileloader' )
             ? $file_loader
             : new WP_Autoload_IsReadableFileLoader;
     }

     /**
      * @param string $class (A full qualified class name)
      *
      * @return bool
      */
     public function load_class( $class ) {

         // performing the psr4 mapping here to get a $file
         $file = '/whatever';

         return $this->file_loader->load_file( $file );
     }

 }
 }}}

 Autoloading rules should depend on a file loader. The file loader receives
 a file name and loads it, if it is present.

 {{{#!php
 <?php
 interface WP_Autoload_FileLoader {

     /**
      * @param string $file
      *
      * @return bool
      */
     public function load_file( $file );
 }
 }}}

 A very simple implementation can just ask for `is_readable()`. Looking on
 performance, another implementation could `glob()`-ing a given directory
 once to read in all PHP-files and matching the given file against this
 list. Even persistent caches are thinkable.

 Last but not least `WP_Autoload_Autoload` acts as a repository for all
 possible rules:

 {{{#!php
 <?php
 interface WP_Autoload_Autoload {

     /**
      * @param WP_Autoload_Rule $autoload_rule
      *
      * @return void
      */
     public function add_rule( $autoload_rule );
 }
 }}}

 The main implementation should be `WP_Autoload_SplAutoload` which connects
 to PHP standard library autoload:

 {{{#!php
 <?php
 class WP_Autoload_SplAutoload implements WP_Autoload_Autoload {

     /**
      * @var WP_Autoload_Rule[]
      */
     private $rules = array();

     /**
      * @param WP_Autoload_Rule $autoload_rule
      *
      * @return void
      */
     public function add_rule( $autoload_rule ) {

         if ( ! in_array( $autoload_rule, $this->rules, TRUE ) )
             $this->rules[] = $autoload_rule;
     }

     /**
      * @param string $fqcn (full qualified class name)
      */
     public function load_class( $fqcn ) {

         foreach ( $this->rules as $rule ) {
             if ( $rule->load_class( $fqcn ) )
                 break;
         }
     }
 }
 }}}

 == Instantiate and propagate the autoloader ==

 {{{#!php
 <?php
 function wp_setup_autoloader() {

     $autoloader = new WP_Autoload_SplAutoload;
     spl_autoload_register( array( $autoloader, 'load_class' ) );

     /**
      * Use the WordPress core autoloader to
      * bootstrap your plugin and theme
      *
      * @param WP_Autoload_Autoload $autoloader
      */
     do_action( 'wp_autoload', $autoloader );
 }
 }}}

 `wp_setup_autoloader()` should then be called early in the WP loading
 process.

 {{{#!php
 <?php
 add_action( 'wp_autoload', function( $autoloader ) {

     $autoloader->addRule(
         new WP_Autoload_Psr4Rule(
             'MyPlugin\\'
             __DIR__ . '/src'
         )
     );
 } );
 }}}

 == Things to discuss ==

  * The proposal uses [http://verraes.net/2013/09/sensible-interfaces/
 sensible interface names] which I prefer over naming interfaces like
 `WhateverInterfaces`. As WordPress does not provide interfaces right now,
 this is just a suggestion.
  * PHP class identifiers are case insensitive. That means `new MySql` and
 `new MySQL` will both work, if a class `mysql` is declared. The autoloader
 should respect this. Now, Psr4 is very wide-spread and encourage
 developers to use case sensitive autoloaders and [https://r.je/php-
 autoloaders-should-not-be-case-sensitive.html this is a problem]. How can
 we deal with this in a performant way?
  * How should the WordPress core files be organized to work with the
 autoloader? Is it realistic to rearrange them, if not how could a
 corresponding `WP_Autoload_Rule` look like?
  * What about compatibility with PHP 5.2.? The proposal uses
 `spl_autoload_register`. But before PHP 5.3.0 it is theoretically possible
 to deactivate the spl extension. In this case another implementation of
 `WP_Autolad_Autoload` would be necessary and maybe some adaptions to the
 other interfaces. But is this really the intention?

 == Finally ==
 Thanks for reading. Feel free to add your concerns, your opinions or even
 if I'm on a completely wrong train. In fact
 I'm really interested in critic. To be clear, I don't want to push this
 proposal but I would like to see a proper
 autoloader in core some day :)

--

Comment (by ocean90):

 Previously: #21300, #30203

--
Ticket URL: <https://core.trac.wordpress.org/ticket/36335#comment:1>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list