xp-framework/rfc

Merge module.xp and autoload.php

Opened this issue · 7 comments

Scope of Change

This RFC suggests merging the special file module.xp into autoload.php created for Composer loading purposes.

Rationale

  • Consistent autoloading whether Composer is used or not; less I/O for the case it is.
  • Syntax-checkable module initialization code
  • Removed confusion with .xp source code from XP Compiler (phase 2)
  • Near zero cost for runtime module reflection (once libraries include module definitions)

Functionality

Current situation

Typical autoload.php file:

<?php namespace xp;

\lang\ClassLoader::registerPath(__DIR__);

An example module.xp:

<?php namespace com\example;

module xp-forge/example {
  public function initialize() {
    echo "Loaded module ", $this->name(), "\n";
  }
}

Implementation

Code inside autoload.php

<?php namespace com\example;

use lang\ClassLoader;

ClassLoader::registerPath(__DIR__, false,'xp-forge/patterns', [
  'initialize' => function() {
    echo '(autoload.php) Loaded module ', $this->name(), "\n";
  }
]);

Loading

  • During composer loading, autoload.php is loaded via autoload -> files from composer.json
  • During XP bootstrapping, autoload.php would be discovered when registering a path. If a module definition is existant, no module.xp file would be checked for.

Phases

  1. XP9.0. No BC breaks exist. You can start using the new layout alongside the old one.
  2. XP10.0. The file module.xp is deprecated, code needs to be migrated to the new layout

See also Transition below.

Security considerations

n/a

Speed impact

Slightly slower for the non-Composer case, since autoload.php files weren't being loaded.

Dependencies

None.

Related documents

Transition

This describes the various filesystem layouts and where module definitions are loaded from.

TL;DR:

If you don't use modules, no need for any action. If you do:

  • If your library requires compatibility with XP < 9.0, use the BC module layout
  • Otherwise use the new module layout only providing autoload.php

Basic Layout

Contains src/main/php/autoload.php

XP8.1 XP8.1 + Composer XP9.0 (+Composer) XP10.0 (+Composer)
Files loaded (none) autoload autoload autoload
Module definition (none) (none) (none) (none)

Classic module layout

Contains src/main/php/autoload.php and src/main/php/module.xp

XP8.1 XP8.1 + Composer XP9.0 (+Composer) XP10.0 (+Composer)
Files loaded module autoload, module autoload, module autoload
Module definition module module module (none) ⚠️

New module layout

Contains src/main/php/autoload.php which defines $module.

XP8.1 XP8.1 + Composer XP9.0 (+Composer) XP10.0 (+Composer)
Files loaded (none) autoload autoload autoload
Module definition (none) (none) autoload autoload

BC module layout

Contains src/main/php/autoload.php which defines $module and src/main/php/module.xp for BC reasons. Note you need to maintain the module code twice in both files!

XP8.1 XP8.1 + Composer XP9.0 (+Composer) XP10.0 (+Composer)
Files loaded module autoload, module autoload autoload
Module definition module module autoload autoload

Example of transition to new functionality:

diff --git a/src/main/php/autoload.php b/src/main/php/autoload.php
index 832c1dd..89375e3 100755
--- a/src/main/php/autoload.php
+++ b/src/main/php/autoload.php
@@ -1,3 +1,18 @@
-<?php namespace xp;
+<?php namespace xp\compiler;

-\lang\ClassLoader::registerPath(__DIR__);
\ No newline at end of file
+use lang\ClassLoader;
+use lang\reflect\Package;
+
+define('MODIFIER_PACKAGE',  2048);
+define('MODIFIER_INLINE',   4096);
+define('MODIFIER_NATIVE',   8192);
+define('DETAIL_PROPERTY',      0);
+
+ClassLoader::registerPath(__DIR__, false, 'xp-framework/compiler', [
+
+  /** @return void */
+  'initialize' => function() {
+    Syntax::registerAll(Package::forName('xp.compiler.syntax')->getPackages());
+    ClassLoader::registerLoader(JitClassLoader::instanceFor(realpath('.')), true);
+  }
+]);

For phase 2, the xp -v output could be changed to show modules instead of class path entries:

$ git diff
diff --git a/src/main/php/xp/runtime/Version.class.php b/src/main/php/xp/runtime/Version.class.php
index c210000..7d950b1 100755
--- a/src/main/php/xp/runtime/Version.class.php
+++ b/src/main/php/xp/runtime/Version.class.php
@@ -1,6 +1,7 @@
 <?php namespace xp\runtime;

 use util\cmd\Console;
+use lang\reflect\Module;

 /**
  * Displays XP version and runtime information
@@ -43,15 +44,15 @@ class Version {
   public static function main(array $args) {
     if (empty($args)) {
       Console::writeLinef(
-        'XP %s { PHP %s & ZE %s } @ %s',
+        "\e[37;1mXP %s { PHP %s & ZE %s } @ %s\e[0m",
         \xp::version(),
         phpversion(),
         zend_version(),
         php_uname()
       );
-      Console::writeLine('Copyright (c) 2001-2016 the XP group');
-      foreach (\lang\ClassLoader::getLoaders() as $delegate) {
-        Console::writeLine($delegate->toString());
+      Console::writeLine('Copyright (c) 2001-2017 the XP group');
+      foreach (Module::$registered as $module) {
+        Console::writeLine($module->name(), "\e[32m: ", $module->classLoader()->toString(), "\e[0m");
       }
       return 1;
     } else {

💡 How about also moving autoload.php from src/main/php to the root directory?

<?php namespace com\example;

use lang\ClassLoader;

ClassLoader::registerPath('src/main/php', false,'xp-forge/patterns', [
  'initialize' => function() {
    echo '(autoload.php) Loaded module ', $this->name(), "\n";
  }
]);

Libraries cannot drop module.xp until they require xp-framework/core 9.0.0 as minimum. Maybe it would be good if 8.x and 7.x would add forward-compatible support for this (7.9.0, 8.3.0) - then library authors could do this:

- "xp-framework/core": "^9.0 | ^8.0 | ^7.0"
+ "xp-framework/core": "^9.0 | ^8.3 | ^7.9"

...and still support XP7 - it's the last version with PHP 5.6 support, necessary for running on Debian Jessie, which bundles this version of PHP by default.

How about also moving autoload.php from src/main/php to the root directory?

It wouldn't be picked up by direct references via class.pth, then:

# Include this library
../inject/src/main/php/

So first we should either start putting module references there, see xp-runners/main#5 or reference autoload.php directly

# Local path and file references
src/autoload.php

# Dependencies, composer-style
?vendor/autoload.php

# Directly referencing dependencies, e.g. during development
../xp/inject/src/autoload.php

The autoload.php files would then:

<?php namespace inject;

use lang\ClassLoader;

ClassLoader::registerPath('src/main/php', false, 'xp-forge/inject', [
  'initialize' => function() {
    echo '(autoload.php) Loaded module ', $this->name(), "\n";
  }
]);
ClassLoader::registerPath('src/test/php', false, 'xp-forge/inject@test');

...or maybe even a file src/module.php, with the following:

<?php namespace inject;

use lang\ClassLoader;

ClassLoader::registerModule(__DIR__, 'xp-forge/inject', ['main/php', 'test/php'], [
  'initialize' => function() {
    echo '(autoload.php) Loaded module ', $this->name(), "\n";
  }
]);