예제 #1
0
 public function testConstructorNormalInstanceCreation()
 {
     $paramOne = 'one';
     $paramTwo = 'two';
     /* @var $service DummyService */
     $service = ServiceManager::getServiceObject(self::SERVICE_CLASS, self::CONTEXT, self::LANGUAGE, [$paramOne, $paramTwo], APFService::SERVICE_TYPE_NORMAL);
     $this->assertEquals($paramOne, $service->getParamOne());
     $this->assertEquals($paramTwo, $service->getParamTwo());
 }
예제 #2
0
 /**
  * Creates the database connection.
  *
  * @param string $context The current context.
  * @param string $language The current language.
  *
  * @return AbstractDatabaseHandler The database connection to read the configuration from and store it.
  *
  * @author Christian Achatz
  * @version
  * Version 0.1, 29.10.2010<br />
  */
 private function &getConnection($context, $language)
 {
     // create service "manually", since we have no convenience method
     /* @var $connMgr ConnectionManager */
     $connMgr =& ServiceManager::getServiceObject(ConnectionManager::class, $context, $language);
     return $connMgr->getConnection($this->connectionName);
 }
예제 #3
0
 /**
  * Returns a service object according to the current application context.
  *
  * @param string $class Fully qualified class name of the service object.
  * @param array $arguments A list of constructor arguments to create the service instance with.
  * @param string $type The initializing type (see service manager for details).
  * @param string $instanceId The id of the instance to return.
  *
  * @return APFObject The desired service object.
  *
  * @author Christian Schäfer
  * @version
  * Version 0.1, 07.03.2007<br />
  * Version 0.2, 08.03.2007 (Context is now taken from the current object)<br />
  * Version 0.3, 10.03.2007 (Method now is considered protected)<br />
  * Version 0.4, 22.04.2007 (Added language initialization of the service manager)<br />
  * Version 0.5, 24.02.2008 (Added the service type param)<br />
  * Version 0.6  21.11.2012 Jens Prangenberg <*****@*****.**> (Added the instance id param)<br />
  */
 protected function &getServiceObject($class, array $arguments = [], $type = APFService::SERVICE_TYPE_SINGLETON, $instanceId = null)
 {
     return ServiceManager::getServiceObject($class, $this->getContext(), $this->getLanguage(), $arguments, $type, $instanceId);
 }
예제 #4
0
 /**
  * Returns the initialized service object.
  *
  * @param string $configNamespace The namespace of the service object.
  * @param string $sectionName The name of the desired service object.
  * @param string $context The context of the current application.
  * @param string $language The language of the current application.
  *
  * @return APFDIService The pre-configured service object.
  * @throws ConfigurationException In case the requested service is not existent.
  * @throws InvalidArgumentException In case of injection issues.
  *
  * @author Christian Achatz
  * @version
  * Version 0.1, 19.04.2009<br />
  * Version 0.2, 24.08.2011 (Added "setupmethod" functionality)<br/>
  * Version 0.3, 07.07.2012 Jan Wiese (Corrected service retrieval to respect context and language each time.<br />
  *                         Introduced CACHED service type to retrieve a NORMAL instance from the cache and thus gain performance for none-singletons.)<br />
  * Version 0.4, 10.07.2012 Jan Wiese (Introduced configuration cache to gain performance)<br />
  * Version 0.5, 10.07.2012 Jan Wiese (Improvements in code quality and removed bugs from v0.3/v0.4)<br />
  */
 public static function &getServiceObject($configNamespace, $sectionName, $context, $language)
 {
     // build cache key. because configuration-file path includes context, include context (and language) in cache key
     // In 2.0 language has been removed from the instance id since within multi-language applications
     // you want to re-use the instance throughout different languages!
     $cacheKey = $configNamespace . '|' . $sectionName . '|' . $context;
     // Check, whether service object was created before. If yes, deliver it from cache for all services types except NORMAL.
     // Do not cache ServiceType 'NORMAL' because we want to have different instances!
     if (isset(self::$SERVICE_OBJECT_CACHE[$cacheKey]) && self::$SERVICE_TYPE_CACHE[$cacheKey] != APFService::SERVICE_TYPE_NORMAL) {
         return self::$SERVICE_OBJECT_CACHE[$cacheKey];
     }
     // Invoke benchmarker. Suppress warning for already started timers with circular calls!
     // Suppressing is here done by a dirty '@', because we will run into an error anyway.
     $t = Singleton::getInstance(BenchmarkTimer::class);
     /* @var $t BenchmarkTimer */
     $benchId = 'DIServiceManager::getServiceObject(' . $configNamespace . ',' . $sectionName . ',' . $context . ',' . $language . ')';
     @$t->start($benchId);
     // Get config to determine, which object to create.
     $config = self::getServiceConfiguration($configNamespace, $context, $language, $cacheKey);
     if (!$config->hasSection($sectionName)) {
         throw new ConfigurationException('[DIServiceManager::getServiceObject()] Service object configuration with ' . 'name "' . $sectionName . '" cannot be found within namespace "' . $configNamespace . '"! Please double-check your setup.', E_USER_ERROR);
     }
     $section = $config->getSection($sectionName);
     // check, whether the section contains the basic directives and read/write service type cache
     if (isset(self::$SERVICE_TYPE_CACHE[$cacheKey])) {
         $serviceType = self::$SERVICE_TYPE_CACHE[$cacheKey];
     } else {
         $serviceType = $section->getValue('servicetype');
         self::$SERVICE_TYPE_CACHE[$cacheKey] = $serviceType;
     }
     $class = $section->getValue('class');
     // The behaviour of service types CACHED and NORMAL is equal in the following, thus remapping it.
     if ($serviceType == APFService::SERVICE_TYPE_CACHED) {
         $serviceType = APFService::SERVICE_TYPE_NORMAL;
     }
     // Check if configuration section was complete. If not throw an exception to fail fast.
     if ($serviceType === null || $class === null) {
         throw new InvalidArgumentException('[DIServiceManager::getServiceObject()] Initialization of the service object "' . $sectionName . '" from namespace "' . $configNamespace . '" cannot be accomplished, due to missing
            or incorrect configuration! Please revise the configuration file and consult the manual!', E_USER_ERROR);
     }
     // Create the service object with use of the "normal" service manager. Perhaps, this
     // may run into problems, because we have to ensure, that the singleton objects are
     // only treated once by the injection mechanism!
     // But: if we constitute, that the injected service objects are often also singletons
     // and the DIServiceManager caches the created service objects within a singleton cache,
     // this is no problem. Hence, the injected instance is then only one time constructed.
     // ID#249: as of 3.0 you are also able to inject dependent configuration and services via constructor.
     // This replaces the former getAndInitServiceObject() method of the ServiceManager with a more elegant
     // way. Configuration allows any number of constructor arguments as string, array, or other services.
     $arguments = [];
     if ($section->hasSection('construct')) {
         $constructorArguments = $section->getSection('construct');
         foreach ($constructorArguments->getSectionNames() as $argumentKey) {
             $directive = $constructorArguments->getSection($argumentKey);
             // be aware of the params needed for injection
             $value = $directive->getValue('value');
             $namespace = $directive->getValue('namespace');
             $name = $directive->getValue('name');
             if ($value === null && ($namespace === null || $name === null)) {
                 throw new InvalidArgumentException('[DIServiceManager::getServiceObject()] Construction of the' . ' service object "' . $sectionName . '" cannot be accomplished, due to' . ' incorrect constructor injection configuration! Please revise the "' . $argumentKey . '" sub section and consult the manual!', E_USER_ERROR);
             }
             // Simple value argument (string)
             if ($directive->hasValue('value')) {
                 $arguments[] = $value;
             } else {
                 // complex injection with another service
                 $arguments[] =& self::getServiceObject($namespace, $name, $context, $language);
             }
         }
     }
     /* @var $serviceObject APFDIService */
     $serviceObject =& ServiceManager::getServiceObject($class, $context, $language, $arguments, $serviceType, $cacheKey);
     // do param injection (static configuration)
     if ($section->hasSection('conf')) {
         $cfTasks = $section->getSection('conf');
         foreach ($cfTasks->getSectionNames() as $initKey) {
             $directive = $cfTasks->getSection($initKey);
             // be aware of the params needed for injection
             $method = $directive->getValue('method');
             if ($method === null) {
                 throw new InvalidArgumentException('[DIServiceManager::getServiceObject()] Initialization of the' . ' service object "' . $sectionName . '" cannot be accomplished, due to' . ' incorrect configuration! Please revise the "' . $initKey . '" sub section and' . ' consult the manual!', E_USER_ERROR);
             }
             // check, if method exists to avoid fatal errors
             if (!method_exists($serviceObject, $method)) {
                 throw new InvalidArgumentException('[DIServiceManager::getServiceObject()] Injection of' . ' configuration value "' . $directive->getValue('value') . '" cannot be accomplished' . ' to service object "' . $class . '"! Method ' . $method . '() is not implemented!', E_USER_ERROR);
             }
             if (($value = $directive->getValue('value')) !== null) {
                 $serviceObject->{$method}($value);
             } else {
                 if (!$directive->hasSection('value')) {
                     throw new InvalidArgumentException('[DIServiceManager::getServiceObject()] Initialization of the' . ' service object "' . $sectionName . '" cannot be accomplished, due to' . ' missing value(s) for method ' . $method . '()! Please revise the "' . $initKey . '" sub section and' . ' consult the manual!', E_USER_ERROR);
                 }
                 $cfSubSection = $directive->getSection('value');
                 $values = [];
                 foreach ($cfSubSection->getValueNames() as $valueName) {
                     $values[] = $cfSubSection->getValue($valueName);
                 }
                 call_user_func_array([$serviceObject, $method], $values);
             }
         }
     }
     // do service object injection
     if ($section->hasSection('init')) {
         $miTasks = $section->getSection('init');
         foreach ($miTasks->getSectionNames() as $initKey) {
             $directive = $miTasks->getSection($initKey);
             // be aware of the params needed for injection
             $method = $directive->getValue('method');
             $namespace = $directive->getValue('namespace');
             $name = $directive->getValue('name');
             if ($method === null || $namespace === null || $name === null) {
                 throw new InvalidArgumentException('[DIServiceManager::getServiceObject()] Initialization of the service object "' . $sectionName . '" cannot be accomplished, due to incorrect configuration! Please revise the "' . $initKey . '" sub section and consult the manual!', E_USER_ERROR);
             }
             // check for circular injection
             $injectionKey = $namespace . '::' . $class . '[' . $serviceType . ']' . ' injected with ' . $method . '(' . $namespace . '::' . $name . ')';
             // TODO why do we accept loops for normal services?
             if (isset(self::$INJECTION_CALL_CACHE[$injectionKey]) && $serviceType != APFService::SERVICE_TYPE_NORMAL) {
                 // append error to log to provide debugging information
                 $log = Singleton::getInstance(Logger::class);
                 /* @var $log Logger */
                 $instructions = '';
                 foreach (self::$INJECTION_CALL_CACHE as $injectionInstruction => $DUMMY) {
                     $instructions .= PHP_EOL . $injectionInstruction;
                 }
                 $log->addEntry(new SimpleLogEntry(Registry::retrieve('APF\\core', 'InternalLogTarget'), '[DIServiceManager::getServiceObject()] Injection stack trace: ' . $instructions, LogEntry::SEVERITY_TRACE));
                 // print note with shorted information
                 throw new InvalidArgumentException('[DIServiceManager::getServiceObject()] Detected circular injection! ' . 'Class "' . $class . '" from namespace "' . $namespace . '" with service type "' . $serviceType . '" was already configured with service object "' . $name . '" from namespace "' . $namespace . '"! Full stack trace can be taken from the logfile!', E_USER_ERROR);
             } else {
                 // add the current run to the recursion detection array
                 self::$INJECTION_CALL_CACHE[$injectionKey] = true;
                 // get the dependent service object
                 $miObject =& self::getServiceObject($namespace, $name, $context, $language);
                 // inject the current service object with the created one
                 if (method_exists($serviceObject, $method)) {
                     $serviceObject->{$method}($miObject);
                 } else {
                     throw new InvalidArgumentException('[DIServiceManager::getServiceObject()] Injection of service object "' . $name . '" from namespace "' . $namespace . '" cannot be accomplished to service object "' . $class . '" from namespace "' . $namespace . '"! Method ' . $method . '() is not implemented!', E_USER_ERROR);
                 }
             }
         }
     }
     // Often, there you have a services that depends on several other services (e.g. database connection). Thus,
     // you are forced to initialize your service using these components. To ease such cases, you may specify a
     // generic method within the "setupmethod" attribute. The DIServiceManager calls this method at the end of
     // the initialization process and you can initialize your service without being dependent on the user's
     // order of configuration parameters.
     // in order to not execute the setup method on every request, check the initialization status of the service
     // object before. this mechanism can be used for re-initialization on __wakeup() in case the property is
     // set to false (=reinitialization after session wake-up).
     $setupMethod = $section->getValue('setupmethod');
     if (!empty($setupMethod)) {
         if (!$serviceObject->isInitialized()) {
             if (method_exists($serviceObject, $setupMethod)) {
                 $serviceObject->{$setupMethod}();
             } else {
                 throw new InvalidArgumentException('[DIServiceManager::getServiceObject()] Custom service object setup ' . 'method "' . $setupMethod . '()" is not implemented for given type "' . get_class($serviceObject) . '"! Please double-check your configuration ' . 'for service "' . $sectionName . '" from namespace "' . $configNamespace . '."', E_USER_ERROR);
             }
         }
     }
     $t->stop($benchId);
     // add service object to cache and return it
     self::$SERVICE_OBJECT_CACHE[$cacheKey] = $serviceObject;
     return self::$SERVICE_OBJECT_CACHE[$cacheKey];
 }