Mwjames has uploaded a new change for review.
https://gerrit.wikimedia.org/r/81537
Change subject: [DIC]: Introduce a simple service locator (support deferred
object registration)
......................................................................
[DIC]: Introduce a simple service locator (support deferred object registration)
Not all the time all service objects are necessary therefore this change
introduces a basic service locator which tries to match a service to
a specifc class name pattern "\SMW\Di<service name>" and registered
it with the container if requested.
Change-Id: Icc9be5a2e4439a89e38d7adfa544f0abfd41d661
---
M SemanticMediaWiki.classes.php
M includes/dic/BaseDependencyContainer.php
M includes/dic/DependencyContainer.php
M includes/dic/SharedDependencyContainer.php
M includes/dic/SimpleDependencyBuilder.php
A includes/dic/objects/DiParserData.php
M tests/phpunit/includes/dic/SimpleDependencyBuilderTest.php
7 files changed, 190 insertions(+), 83 deletions(-)
git pull
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/SemanticMediaWiki
refs/changes/37/81537/1
diff --git a/SemanticMediaWiki.classes.php b/SemanticMediaWiki.classes.php
index ff8ba45..747b6f2 100644
--- a/SemanticMediaWiki.classes.php
+++ b/SemanticMediaWiki.classes.php
@@ -92,6 +92,9 @@
'SMW\DependencyRequestor' =>
'includes/dic/DependencyRequestor.php',
'SMW\DependencyInjector' =>
'includes/dic/DependencyInjector.php',
+ // Service objects
+ 'SMW\DiParserData' =>
'includes/dic/objects/DiParserData.php',
+
'SMW\Cacheable' => 'includes/Cacheable.php',
'SMW\Configurable' => 'includes/Configurable.php',
'SMW\StoreAccess' => 'includes/StoreAccess.php',
diff --git a/includes/dic/BaseDependencyContainer.php
b/includes/dic/BaseDependencyContainer.php
index bc2e827..2dad268 100644
--- a/includes/dic/BaseDependencyContainer.php
+++ b/includes/dic/BaseDependencyContainer.php
@@ -17,6 +17,23 @@
* Implements the DependencyContainer interface and is responsible for handling
* object storage, and retrieval of object definitions
*
+ * @par Example:
+ * @code
+ * $container = new EmptyDependencyContainer()
+ *
+ * // Eager loading (do everything when asked)
+ * $container->title = new Title() or
+ * $container->registerObject( 'Title', new Title() )
+ *
+ * // Lazy loading (only do an instantiation when required)
+ * $container->diWikiPage = function ( DependencyBuilder $builder ) {
+ * return DIWikiPage::newFromTitle( $builder->getArgument( 'Title' ) );
+ * } ) or
+ * $container->registerObject( 'DIWikiPage', function ( DependencyBuilder
$builder ) {
+ * return DIWikiPage::newFromTitle( $builder->getArgument( 'Title' ) );
+ * } );
+ * @endcode
+ *
* @ingroup DependencyContainer
*/
abstract class BaseDependencyContainer extends ObjectStorage implements
DependencyContainer {
@@ -82,19 +99,6 @@
/**
* Register an object via magic method __set
*
- * @par Example:
- * @code
- * $container = new EmptyDependencyContainer()
- *
- * // Eager loading (do everything when asked)
- * $container->title = new Title() or
- *
- * // Lazy loading (only do an instanitation when required)
- * $container->diWikiPage = function ( DependencyBuilder $builder ) {
- * return DIWikiPage::newFromTitle( $builder->getArgument( 'Title' )
);
- * } );
- * @endcode
- *
* @since 1.9
*
* @param string $objectName
@@ -107,32 +111,14 @@
/**
* Register an object
*
- * @par Example:
- * @code
- * $container = new EmptyDependencyContainer()
- *
- * // Eager loading (do everything when asked)
- * $container->registerObject( 'Title', new Title() ) or
- *
- * // Lazy loading (only do an instanitation when required)
- * $container->registerObject( 'DIWikiPage', function (
DependencyBuilder $builder ) {
- * return DIWikiPage::newFromTitle( $builder->getArgument( 'Title' )
);
- * } );
- * @endcode
- *
* @since 1.9
*
* @param string $objectName
- * @param mixed $signature
+ * @param mixed $objectSignature an arbitrary signature of any kind
such Closure, DependencyObject etc.
+ * @param mixed $objectScope
*/
- public function registerObject( $objectName, $objectSignature,
$objectScope = self::SCOPE_PROTOTYPE ) {
- $this->set( $objectName, array( $objectSignature, $objectScope
) );
+ public function registerObject( $objectName, $objectSignature,
$objectScope = DependencyObject::SCOPE_PROTOTYPE ) {
+ $this->attach( $objectName, array( $objectSignature,
$objectScope ) );
}
- // Not sure that the overhead would warrant something like
- // $definition = new DependencyObjectDefinition( $name, $signature,
$scope )
- //
- // public function registerObject( DependencyObjectDefinition
$definition ) {
- // $this->set( $definition->getName(), array(
$definition->getSignature() , $definition->getScope() ) );
- // }
}
diff --git a/includes/dic/DependencyContainer.php
b/includes/dic/DependencyContainer.php
index e84190b..34216fc 100644
--- a/includes/dic/DependencyContainer.php
+++ b/includes/dic/DependencyContainer.php
@@ -27,11 +27,13 @@
const SCOPE_SINGLETON = 1;
/**
- * Register a dependency object
+ * Defines an object definition
*
* @since 1.9
+ *
+ * @param DependencyBuilder $builder
*/
- public function registerObject( $objectName, $objectSignature,
$objectScope );
+ public function define( DependencyBuilder $builder );
}
@@ -40,4 +42,13 @@
*
* @ingroup DependencyContainer
*/
-interface DependencyContainer extends DependencyObject, Accessible,
Changeable, Combinable {}
+interface DependencyContainer extends Accessible, Changeable, Combinable {
+
+ /**
+ * Register a dependency object
+ *
+ * @since 1.9
+ */
+ public function registerObject( $objectName, $objectSignature,
$objectScope );
+
+}
diff --git a/includes/dic/SharedDependencyContainer.php
b/includes/dic/SharedDependencyContainer.php
index c1e0e70..329df27 100644
--- a/includes/dic/SharedDependencyContainer.php
+++ b/includes/dic/SharedDependencyContainer.php
@@ -44,7 +44,7 @@
*/
$this->registerObject( 'Settings', function () {
return Settings::newFromGlobals();
- }, self::SCOPE_SINGLETON );
+ }, DependencyObject::SCOPE_SINGLETON );
/**
* Store object definition
@@ -55,7 +55,7 @@
*/
$this->registerObject( 'Store', function ( DependencyBuilder
$builder ) {
return StoreFactory::getStore( $builder->newObject(
'Settings' )->get( 'smwgDefaultStore' ) );
- }, self::SCOPE_SINGLETON );
+ }, DependencyObject::SCOPE_SINGLETON );
/**
* CacheHandler object definition
@@ -66,26 +66,7 @@
*/
$this->registerObject( 'CacheHandler', function (
DependencyBuilder $builder ) {
return CacheHandler::newFromId( $builder->newObject(
'Settings' )->get( 'smwgCacheType' ) );
- }, self::SCOPE_SINGLETON );
-
- /**
- * ParserData object definition
- *
- * @since 1.9
- *
- * @return ParserData
- */
- $this->registerObject( 'ParserData', function (
DependencyBuilder $builder ) {
-
- $parserData = new ParserData(
- $builder->getArgument( 'Title' ),
- $builder->getArgument( 'ParserOutput' )
- );
-
- $parserData->setObservableDispatcher(
$builder->newObject( 'ObservableUpdateDispatcher' ) );
-
- return $parserData;
- } );
+ }, DependencyObject::SCOPE_SINGLETON );
/**
* UpdateObserver object definitions
@@ -121,20 +102,6 @@
$this->registerObject( 'NamespaceExaminer', function (
DependencyBuilder $builder ){
return NamespaceExaminer::newFromArray(
$builder->newObject( 'Settings' )->get( 'smwgNamespacesWithSemanticLinks' ) );
} );
-
- // $this->set( 'FactboxPresenter', function ( DependencyBuilder
$builder ) {
- // $outputPage = $builder->getArgument( 'OutputPage' );
- // return new FactboxPresenter( $outputPage,
$builder->newObject( 'Settings' ) );
- // } );
-
- // $this->set( 'Factbox', function ( DependencyBuilder $builder
) {
- // return new Factbox(
- // $builder->newObject( 'Store' ),
- // $builder->getArgument( 'SMW\ParserData' ),
- // $builder->getArgument( 'SMW\Settings' ),
- // $builder->getArgument( 'RequestContext' )
- // );
- // } );
}
diff --git a/includes/dic/SimpleDependencyBuilder.php
b/includes/dic/SimpleDependencyBuilder.php
index 833c739..af51069 100644
--- a/includes/dic/SimpleDependencyBuilder.php
+++ b/includes/dic/SimpleDependencyBuilder.php
@@ -251,12 +251,22 @@
}
if ( !$this->dependencyContainer->has( $objectName ) ) {
- throw new OutOfBoundsException( "{$objectName} is not
registered" );
+
+ // The service object is not registered with the
container, try to
+ // locate the corresponding object and register it with
the container
+ if ( !$this->locatedObject( $objectName ) ) {
+ throw new OutOfBoundsException( "{$objectName}
is not registered or available as service object" );
+ };
+
}
list( $objectSignature, $objectScope ) =
$this->dependencyContainer->get( $objectName );
- return $this->load( $objectName, $objectSignature, $objectScope
);
+ if ( $objectSignature instanceOf DependencyObject ) {
+ $objectSignature = $objectSignature->define( $this );
+ }
+
+ return $this->loadObject( $objectName, $objectSignature,
$objectScope );
}
/**
@@ -274,7 +284,7 @@
*
* @return mixed
*/
- private function load( $objectName, $objectSignature, $objectScope ) {
+ private function loadObject( $objectName, $objectSignature,
$objectScope ) {
// An object scope invoked during the build process has
priority over
// the original scope definition
@@ -320,4 +330,36 @@
}
+ /**
+ * It is a basic service locator which needs to adhere certain
requirements to
+ * work within some performance parameters
+ *
+ * It is expected that the requested service object is being available
using the
+ * following SMW\Di + <requested service> pattern
+ *
+ * @param string $objectName
+ *
+ * @return boolean
+ */
+ private function locatedObject( $objectName ) {
+
+ // Simple pattern match otherwise the ReflectorClass would be
needed
+ // to allow a more sophisticated approach which also would drain
+ // on injection performance
+ $objectClass = "\SMW\Di{$objectName}";
+
+ if( class_exists( $objectClass ) ) {
+
+ $objectSignature = new $objectClass;
+
+ if ( $objectSignature instanceOf DependencyObject ) {
+ $this->dependencyContainer->registerObject(
$objectName, $objectSignature );
+ return true;
+ }
+
+ }
+
+ return false;
+ }
+
}
diff --git a/includes/dic/objects/DiParserData.php
b/includes/dic/objects/DiParserData.php
new file mode 100644
index 0000000..918c697
--- /dev/null
+++ b/includes/dic/objects/DiParserData.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace SMW;
+
+/**
+ * Provides interfaces for dependency injection
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * Interface specifying a dependency object
+ *
+ * @ingroup DependencyObject
+ */
+class DiParserData implements DependencyObject {
+
+ /**
+ * @see DependencyObject::define
+ *
+ * @since 1.9
+ *
+ * @param DependencyBuilder $builder
+ */
+ public function define( DependencyBuilder $builder ) {
+
+ $parserData = new ParserData(
+ $builder->getArgument( 'Title' ),
+ $builder->getArgument( 'ParserOutput' )
+ );
+
+ $parserData->setObservableDispatcher( $builder->newObject(
'ObservableUpdateDispatcher' ) );
+
+ return $parserData;
+
+ }
+
+}
diff --git a/tests/phpunit/includes/dic/SimpleDependencyBuilderTest.php
b/tests/phpunit/includes/dic/SimpleDependencyBuilderTest.php
index 6186ecb..2d302fb 100644
--- a/tests/phpunit/includes/dic/SimpleDependencyBuilderTest.php
+++ b/tests/phpunit/includes/dic/SimpleDependencyBuilderTest.php
@@ -44,6 +44,18 @@
}
/**
+ * Helper method that returns a scope definition
+ *
+ * @since 1.9
+ *
+ * @param $data
+ */
+ protected function getScopeDefinition( $scope ) {
+ $reflector = $this->newReflector( '\SMW\DependencyObject' );
+ return $reflector->getConstant( $scope );
+ }
+
+ /**
* Helper method that returns a DependencyContainer object
*
* @since 1.9
@@ -385,9 +397,7 @@
public function testCompareScope( $setup, $expected ) {
$instance = $this->newInstance();
- $container = $instance->getContainer();
- $reflector = $this->newReflector( get_class( $container ) );
- $scope = $reflector->getConstant( $setup['scope'] );
+ $scope = $this->getScopeDefinition( $setup['scope'] );
$title = $this->newTitle( NS_MAIN, 'Lila' );
// Lazy loading or deferred instantiation
@@ -583,9 +593,7 @@
public function testSetCallMagicWordScope( $setup, $expected ) {
$instance = $this->newInstance();
- $container = $instance->getContainer();
- $reflector = $this->newReflector( get_class( $container ) );
- $scope = $reflector->getConstant( $setup['scope'] );
+ $scope = $this->getScopeDefinition( $setup['scope'] );
$title = $this->newTitle( NS_MAIN, 'Lula' );
$instance->getContainer()->DIWikiPage = function() use( $title
) {
@@ -642,6 +650,37 @@
$this->assertFalse(
$instance->getContainer()->has( 'Title' ),
'asserts that after removal the container does not have
a particular object definition'
+ );
+
+ }
+
+ /**
+ * @test SimpleDependencyBuilder::newObject
+ * @dataProvider dependencyObjectDataProvider
+ *
+ * @since 1.9
+ *
+ * @param $setup
+ * @param $expected
+ */
+ public function testDependencyObjectInvokation( $setup, $expected ) {
+
+ $dependencyObject = $this->getMockBuilder(
'\SMW\DependencyObject' )
+ ->disableOriginalConstructor()
+ ->setMethods( array( 'define' ) )
+ ->getMock();
+
+ $dependencyObject->expects( $this->any() )
+ ->method( 'define' )
+ ->will( $this->returnValue( $setup ) );
+
+ $instance = $this->newInstance();
+ $instance->getContainer()->registerObject( 'Quux',
$dependencyObject );
+
+ $this->assertEquals(
+ $expected,
+ $instance->newObject( 'Quux' ),
+ 'asserts whether object ...'
);
}
@@ -748,4 +787,20 @@
return $provider;
}
+ /**
+ * @return array
+ */
+ public function dependencyObjectDataProvider() {
+
+ $provider = array();
+
+ $stdClass = new \stdClass;
+ $closure = function() use( $stdClass ) { return $stdClass; };
+
+ $provider[] = array( $stdClass, $stdClass );
+ $provider[] = array( $closure, $stdClass );
+
+ return $provider;
+ }
+
}
--
To view, visit https://gerrit.wikimedia.org/r/81537
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Icc9be5a2e4439a89e38d7adfa544f0abfd41d661
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/SemanticMediaWiki
Gerrit-Branch: master
Gerrit-Owner: Mwjames <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits