-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Scope of Change
Every class path element will be able to provide a way to provide meta information. When it does so, we will call this class path element a module. Modules are named, may have a version, are accessible by reflection, annotatable and may contain static initializer blocks.
Rationale
Prerequisite for XP Framework Modularization - see RFC #204.
Functionality
If a classpath element (a path or a XAR file) contains a file named module.xp, then it is loaded and initialized. For each loaded module, a lang.Module instance is created to access the module's meta data and wrap around the class loader for the specific classpath element.
The "core" module
The XP Framework's core will be represented by a module named core.
Reflection
Here's the lang.Module class.
public class lang.Module extends lang.Object implements lang.IClassLoader {
protected var lang.Module::$loader
protected var lang.Module::$definition
protected var lang.Module::$name
protected var lang.Module::$version
public lang.Module __construct(lang.IClassLoader $loader, lang.XPClass $definition, [string $name= null], [string $version= null])
public static lang.reflect.Module forName(string $name) throws lang.ElementNotFoundException
public static lang.reflect.Module[] getModules()
public bool providesClass(string $class)
public bool providesResource(string $filename)
public bool providesPackage(string $package)
public string[] packageContents(string $package)
public lang.XPClass loadClass(string $class) throws lang.ClassNotFoundException
public string loadClass0(string $class) throws lang.ClassNotFoundException
public string getResource(string $string) throws lang.ElementNotFoundException
public io.Stream getResourceAsStream(string $string) throws lang.ElementNotFoundException
public string getName()
public string getVersion()
public string getComment()
public bool hasAnnotation(string $name, [string $key= null])
public var getAnnotation(string $name, [string $key= null]) throws lang.ElementNotFoundException
public bool hasAnnotations()
public var[] getAnnotations()
public lang.IClassLoader getClassLoader()
public bool equals(var $cmp)
public string toString([string $cwd= null])
public string hashCode()
public string getClassName()
public lang.XPClass getClass()
}Example module.xp
<?php
/* This file is part of the XP framework
*
* $Id$
*/
uses(...);
/**
* The example module
*
* @see http://example.com
*/
module example(1.0.0) {
// Class body here
}
?>Restrictions
- Module names must match the following regular expression:
/[a-z][a-z0-9_\/\.-]*/(all-lowercase, start with an alpha char, and continue with any alphanumeric, underscore, forward slash, dot, or dash). It is recommended to use the pattern[vendor] "/" [name]in order to easily integrate with Composer - Module versions may contain anything except these special chars:
(,),*,+,-,<,>,[,],,and the combination.., although it is highly recommended to stick to what PHP's version_compare() understands.
Usecase: Database drivers
Database drivers can be modularized as follows when this RFC is implemented:
/path/to/sybase_ct/src/main/php/module.xp:
<?php
uses('rdbms.DriverManager');
module sybase_ct(1.0.3) {
public static function initialize(IClassLoader $l) {
DriverManager::register('sybase', $l->loadClass('rdbms.sybase.SybaseConnection'));
}
}
?>/path/to/sybase_ct/src/main/php/rdbms/sybase/:
* SybaseConnection.class.php
* SybaseResultSet.class.php
* SybaseDialect.class.php
In the application's class path, we can include /path/to/sybase_ct/src/main/php/ and will automatically have the sybase driver registered.
Usecase: IoC
IoC containers need a place where the wiring takes place. This can now be done in the static initializer of a module:
/path/to/app/src/main/php/module.xp:
<?php
uses('ioc.Binder');
module company/db-app {
static function __static() {
with ($binder= new Binder()); {
$binder->bind('rdbms.DBConnection')->named('lsd')->to(DriverManager::getConnection('...'));
}
}
}
?>/path/to/app/src/main/php/com/example/scriptlet/state:
* HomeState.class.php
* ...
Later on, we can use:
<?php
class HomeState extends AbstractState {
#[@inject, @named('lsd')]
public function setConnection(DBConnection $conn) { ... }
public function process($request, $response, $context) { ... }
}
?>...and have the database connection injected.
Security considerations
n/a
Speed impact
Slightly slower because loading a class path will add an addition stat() call.
Class loading optimization
Not all modules need to be searched for all classes, resources or packages. Modules can optionally declare packages they provide, making the class loading mechanism skip them during class lookup based on string comparisons rather than filesystem lookups.
<?php
module imaging.test(3.4.1) provides imaging.tests.unittest, imaging.tests.integration {
}
?>Dependencies
None.
Related documents
RFC #204
http://effbot.org/pyfaq/what-is-init-py-used-for.htm