From 3b0ad843ff3c02a92e51668d234d2354ca36fc9a Mon Sep 17 00:00:00 2001
From: Marco Stoll <marco.stoll@rocketmail.com>
Date: Fri, 21 Jun 2019 10:22:37 +0200
Subject: [PATCH] [CHANGE] use new FF-Factories features

---
 composer.json                                 |   3 +-
 src/Services/AbstractService.php              |  25 +-
 src/Services/Factories/ServicesFactory.php    |  11 +-
 .../{ => Traits}/ServiceLocatorTrait.php      |   5 +-
 tests/Services/AbstractServiceTest.php        |   8 +
 .../Factories/ServicesFactoryTest.php         | 224 ++++++++++--------
 6 files changed, 164 insertions(+), 112 deletions(-)
 rename src/Services/{ => Traits}/ServiceLocatorTrait.php (85%)

diff --git a/composer.json b/composer.json
index c444b5a..97a09de 100644
--- a/composer.json
+++ b/composer.json
@@ -14,7 +14,8 @@
   ],
   "require": {
     "php": ">=7.2",
-    "fastforward/factories": "^1.0.0"
+    "fastforward/data-structures": "^1.0.0",
+    "fastforward/factories": "^1.2.0"
   },
   "require-dev": {
     "phpunit/phpunit": "^8"
diff --git a/src/Services/AbstractService.php b/src/Services/AbstractService.php
index 29f1495..8c5ab33 100644
--- a/src/Services/AbstractService.php
+++ b/src/Services/AbstractService.php
@@ -10,16 +10,24 @@ declare(strict_types=1);
 
 namespace FF\Services;
 
+use FF\Factories\ClassLocators\ClassIdentifierAwareInterface;
 use FF\Services\Exceptions\ConfigurationException;
+use FF\Services\Traits\EventEmitterTrait;
+use FF\Services\Traits\ServiceLocatorTrait;
 
 /**
  * Class AbstractService
  *
  * @package FF\Services
  */
-abstract class AbstractService
+abstract class AbstractService implements ClassIdentifierAwareInterface
 {
-    use ServiceLocatorTrait;
+    use EventEmitterTrait, ServiceLocatorTrait;
+
+    /**
+     * For use with the BaseNamespaceClassLocator of the ServicesFactory
+     */
+    const COMMON_NS_SUFFIX = 'Services';
 
     /**
      * @var array
@@ -57,6 +65,19 @@ abstract class AbstractService
         return $this->options[$key] ?? $default;
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public static function getClassIdentifier(): string
+    {
+        $className = get_called_class();
+        $needle = '\\' . self::COMMON_NS_SUFFIX . '\\';
+        $pos = strpos($className, $needle);
+        if ($pos === false) return $className;
+
+        return substr($className, $pos + strlen($needle));
+    }
+
     /**
      * Initializes the service
      *
diff --git a/src/Services/Factories/ServicesFactory.php b/src/Services/Factories/ServicesFactory.php
index 9c0b150..b528cc0 100644
--- a/src/Services/Factories/ServicesFactory.php
+++ b/src/Services/Factories/ServicesFactory.php
@@ -11,8 +11,8 @@ declare(strict_types=1);
 namespace FF\Services\Factories;
 
 use FF\Factories\AbstractSingletonFactory;
+use FF\Factories\ClassLocators\BaseNamespaceClassLocator;
 use FF\Factories\ClassLocators\ClassLocatorInterface;
-use FF\Factories\ClassLocators\NamespaceClassLocator;
 use FF\Factories\Exceptions\ClassNotFoundException;
 use FF\Services\AbstractService;
 use FF\Services\Exceptions\ConfigurationException;
@@ -35,13 +35,14 @@ class ServicesFactory extends AbstractSingletonFactory
     protected $servicesOptions;
 
     /**
-     * Uses a NamespaceClassLocator pre-configured with the FF\Services namespace.
+     * Uses a BaseNamespaceClassLocator pre-configured with the 'Services' as common suffix and the FF namespace.
+     *
      * @param array $servicesOptions
-     * @see \FF\Factories\ClassLocators\NamespaceClassLocator
+     * @see \FF\Factories\ClassLocators\BaseNamespaceClassLocator
      */
     public function __construct(array $servicesOptions = [])
     {
-        parent::__construct(new NamespaceClassLocator(__NAMESPACE__));
+        parent::__construct(new BaseNamespaceClassLocator(AbstractService::COMMON_NS_SUFFIX, 'FF'));
 
         $this->servicesOptions = $servicesOptions;
     }
@@ -117,7 +118,7 @@ class ServicesFactory extends AbstractSingletonFactory
 
     /**
      * {@inheritDoc}
-     * @return NamespaceClassLocator
+     * @return BaseNamespaceClassLocator
      */
     public function getClassLocator(): ClassLocatorInterface
     {
diff --git a/src/Services/ServiceLocatorTrait.php b/src/Services/Traits/ServiceLocatorTrait.php
similarity index 85%
rename from src/Services/ServiceLocatorTrait.php
rename to src/Services/Traits/ServiceLocatorTrait.php
index b32fc8a..4dde715 100644
--- a/src/Services/ServiceLocatorTrait.php
+++ b/src/Services/Traits/ServiceLocatorTrait.php
@@ -8,14 +8,15 @@
  */
 declare(strict_types=1);
 
-namespace FF\Services;
+namespace FF\Services\Traits;
 
+use FF\Services\AbstractService;
 use FF\Services\Factories\SF;
 
 /**
  * Trait ServiceLocatorTrait
  *
- * @package FF\Services
+ * @package FF\Services\Traits
  */
 trait ServiceLocatorTrait
 {
diff --git a/tests/Services/AbstractServiceTest.php b/tests/Services/AbstractServiceTest.php
index 37090d7..067f7d9 100644
--- a/tests/Services/AbstractServiceTest.php
+++ b/tests/Services/AbstractServiceTest.php
@@ -71,6 +71,14 @@ class AbstractServiceTest extends TestCase
 
         new MyService(['foo' => 'baz']);
     }
+
+    /**
+     * Tests the namesake method/feature
+     */
+    public function testGetClassIdentifier()
+    {
+        $this->assertEquals('MyService', MyService::getClassIdentifier());
+    }
 }
 
 class MyService extends AbstractService
diff --git a/tests/Services/Factories/ServicesFactoryTest.php b/tests/Services/Factories/ServicesFactoryTest.php
index 4f7407b..3dce62b 100644
--- a/tests/Services/Factories/ServicesFactoryTest.php
+++ b/tests/Services/Factories/ServicesFactoryTest.php
@@ -8,119 +8,139 @@
  */
 declare(strict_types=1);
 
-namespace FF\Tests\Services\Factories;
+namespace FF\Tests\Services\Factories {
 
-use FF\Factories\Exceptions\ClassNotFoundException;
-use FF\Services\AbstractService;
-use FF\Services\Exceptions\ConfigurationException;
-use FF\Services\Factories\ServicesFactory;
-use PHPUnit\Framework\TestCase;
-
-/**
- * Test ServicesFactoryTest
- *
- * @package FF\Tests
- */
-class ServicesFactoryTest extends TestCase
-{
-    const TEST_OPTIONS = ['ServiceOne' => ['foo' => 'bar']];
+    use FF\Factories\ClassLocators\BaseNamespaceClassLocator;
+    use FF\Factories\Exceptions\ClassNotFoundException;
+    use FF\Services\Exceptions\ConfigurationException;
+    use FF\Services\Factories\ServicesFactory;
+    use FF\Tests\Services\ServiceOne;
+    use FF\Tests\Services\ServiceTwo;
+    use PHPUnit\Framework\TestCase;
 
     /**
-     * {@inheritdoc}
-     */
-    public static function setUpBeforeClass(): void
-    {
-        ServicesFactory::clearInstance();
-    }
-
-    /**
-     * Tests the namesake method/feature
-     */
-    public function testGetInstanceConfigException()
-    {
-        $this->expectException(ConfigurationException::class);
-
-        ServicesFactory::getInstance();
-    }
-
-    /**
-     * Tests the namesake method/feature
-     */
-    public function testSetGetInstance()
-    {
-        $instance = new ServicesFactory(self::TEST_OPTIONS);
-        $instance->getClassLocator()->prependNamespaces(__NAMESPACE__);
-        ServicesFactory::setInstance($instance);
-
-        $this->assertSame($instance, ServicesFactory::getInstance());
-    }
-
-    /**
-     * Tests the namesake method/feature
+     * Test ServicesFactoryTest
      *
-     * @depends testSetGetInstance
+     * @package FF\Tests
      */
-    public function testSetServiceOptions()
+    class ServicesFactoryTest extends TestCase
     {
-        $this->assertEquals(
-            self::TEST_OPTIONS['ServiceOne'],
-            ServicesFactory::getInstance()->getServiceOptions('ServiceOne')
-        );
-        $this->assertEquals([], ServicesFactory::getInstance()->getServiceOptions('unknown'));
-    }
+        const TEST_OPTIONS = ['ServiceOne' => ['foo' => 'bar']];
 
-    /**
-     * Tests the namesake method/feature
-     *
-     * @depends testSetGetInstance
-     */
-    public function testGetSingle()
-    {
-        $service = ServicesFactory::getInstance()->get('ServiceOne');
-        $this->assertInstanceOf(ServiceOne::class, $service);
-        $this->assertEquals(self::TEST_OPTIONS['ServiceOne'], $service->getOptions());
-    }
-
-    /**
-     * Tests the namesake method/feature
-     *
-     * @depends testSetGetInstance
-     */
-    public function testGetMultiples()
-    {
-        $services = ServicesFactory::getInstance()->get('ServiceOne', 'ServiceOne');
-        $this->assertEquals(2, count($services));
-        $this->assertInstanceOf(ServiceOne::class, $services[0]);
-        $this->assertInstanceOf(ServiceOne::class, $services[1]);
-    }
-
-    /**
-     * Tests the namesake method/feature
-     *
-     * @depends testSetGetInstance
-     */
-    public function testGetClassNotFound()
-    {
-        $this->expectException(ClassNotFoundException::class);
-
-        ServicesFactory::getInstance()->get('ServiceUnknown');
-    }
-}
-
-class ServiceOne extends AbstractService
-{
-    protected function validateOptions(array $options, array &$errors): bool
-    {
-        if (isset($options['foo']) && $options['foo'] != 'bar') {
-            $errors[] = 'foo is not bar';
-            return false;
+        /**
+         * {@inheritdoc}
+         */
+        public static function setUpBeforeClass(): void
+        {
+            ServicesFactory::clearInstance();
         }
 
-        return parent::validateOptions($options, $errors);
+        /**
+         * Tests the namesake method/feature
+         */
+        public function testGetInstanceConfigException()
+        {
+            $this->expectException(ConfigurationException::class);
+
+            ServicesFactory::getInstance();
+        }
+
+        /**
+         * Tests the namesake method/feature
+         */
+        public function testSetGetInstance()
+        {
+            $instance = new ServicesFactory(self::TEST_OPTIONS);
+            $instance->getClassLocator()->prependNamespaces('FF\Tests');
+            ServicesFactory::setInstance($instance);
+
+            $this->assertSame($instance, ServicesFactory::getInstance());
+        }
+
+        /**
+         * Tests the namesake method/feature
+         */
+        public function testGetClassLocator()
+        {
+            $this->assertInstanceOf(
+                BaseNamespaceClassLocator::class,
+                ServicesFactory::getInstance()->getClassLocator()
+            );
+        }
+
+        /**
+         * Tests the namesake method/feature
+         *
+         * @depends testSetGetInstance
+         */
+        public function testSetServiceOptions()
+        {
+            $this->assertEquals(
+                self::TEST_OPTIONS['ServiceOne'],
+                ServicesFactory::getInstance()->getServiceOptions('ServiceOne')
+            );
+            $this->assertEquals([], ServicesFactory::getInstance()->getServiceOptions('ServiceTwo'));
+            $this->assertEquals([], ServicesFactory::getInstance()->getServiceOptions('unknown'));
+        }
+
+        /**
+         * Tests the namesake method/feature
+         *
+         * @depends testSetGetInstance
+         */
+        public function testGetSingle()
+        {
+            $service = ServicesFactory::getInstance()->get('ServiceOne');
+            $this->assertInstanceOf(ServiceOne::class, $service);
+            $this->assertEquals(self::TEST_OPTIONS['ServiceOne'], $service->getOptions());
+        }
+
+        /**
+         * Tests the namesake method/feature
+         *
+         * @depends testSetGetInstance
+         */
+        public function testGetMultiples()
+        {
+            $services = ServicesFactory::getInstance()->get('ServiceOne', 'ServiceTwo');
+            $this->assertEquals(2, count($services));
+            $this->assertInstanceOf(ServiceOne::class, $services[0]);
+            $this->assertInstanceOf(ServiceTwo::class, $services[1]);
+        }
+
+        /**
+         * Tests the namesake method/feature
+         *
+         * @depends testSetGetInstance
+         */
+        public function testGetClassNotFound()
+        {
+            $this->expectException(ClassNotFoundException::class);
+
+            ServicesFactory::getInstance()->get('ServiceUnknown');
+        }
     }
 }
 
-class ServiceTwo extends AbstractService
-{
+namespace FF\Tests\Services {
 
+    use FF\Services\AbstractService;
+
+    class ServiceOne extends AbstractService
+    {
+        protected function validateOptions(array $options, array &$errors): bool
+        {
+            if (isset($options['foo']) && $options['foo'] != 'bar') {
+                $errors[] = 'foo is not bar';
+                return false;
+            }
+
+            return parent::validateOptions($options, $errors);
+        }
+    }
+
+    class ServiceTwo extends AbstractService
+    {
+
+    }
 }
\ No newline at end of file