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

Reply via email to