apparat/object

View on GitHub
src/Object/Domain/Repository/Service.php

Summary

Maintainability
A
45 mins
Test Coverage
<?php

/**
 * apparat-object
 *
 * @category    Apparat
 * @package     Apparat\Object
 * @subpackage  Apparat\Object\Domain
 * @author      Joschi Kuphal <joschi@kuphal.net> / @jkphl
 * @copyright   Copyright © 2016 Joschi Kuphal <joschi@kuphal.net> / @jkphl
 * @license     http://opensource.org/licenses/MIT The MIT License (MIT)
 */

/***********************************************************************************
 *  The MIT License (MIT)
 *
 *  Copyright © 2016 Joschi Kuphal <joschi@kuphal.net> / @jkphl
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
 *  this software and associated documentation files (the "Software"), to deal in
 *  the Software without restriction, including without limitation the rights to
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 *  the Software, and to permit persons to whom the Software is furnished to do so,
 *  subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 ***********************************************************************************/

namespace Apparat\Object\Domain\Repository;

use Apparat\Object\Domain\Model\Object\ManagerInterface;
use Apparat\Object\Domain\Model\Uri\ApparatUrl;
use Apparat\Object\Domain\Model\Uri\ObjectUrl;
use Apparat\Object\Module;

/**
 * Repository register
 *
 * @package Apparat\Object
 * @subpackage Apparat\Object\Domain
 */
class Service
{
    /**
     * Registered repositories
     *
     * @var array
     */
    protected $registry = [];
    /**
     * Repository auto-connector service
     *
     * @var AutoConnectorInterface
     */
    protected $autoConnector = null;
    /**
     * Adapter strategy factory
     *
     * @var AdapterStrategyFactoryInterface
     */
    protected $adptStrategyFactory = null;
    /**
     * Object manager
     *
     * @var ManagerInterface
     */
    protected $objectManager = null;
    /**
     * Auto-connect to repositories
     *
     * @var bool
     */
    protected $autoConnectEnabled = true;

    /*******************************************************************************
     * PUBLIC METHODS
     *******************************************************************************/

    /**
     * Repository service constructor
     *
     * @param AutoConnectorInterface $autoConnector Auto-connector
     * @param AdapterStrategyFactoryInterface $adptStrategyFactory Adapter strategy factory
     * @param ManagerInterface $objectManager Object manager
     */
    public function __construct(
        AutoConnectorInterface $autoConnector,
        AdapterStrategyFactoryInterface $adptStrategyFactory,
        ManagerInterface $objectManager
    ) {
        $this->autoConnector = $autoConnector;
        $this->adptStrategyFactory = $adptStrategyFactory;
        $this->objectManager = $objectManager;
    }

    /**
     * Reset the repository service
     *
     * @return Service Self reference
     */
    public function reset()
    {
        $this->registry = [];
        return $this;
    }

    /**
     * Pre-register a repository
     *
     * The purpose of repository pre-registration is to provide custom arguments (like a base
     * directory or basic authentication credentials.
     * The repository URL may be local or remote, relative or absolute, with Apparat or HTTP scheme.
     *
     * @param string|ObjectUrl $url Repository URL
     * @param RepositoryInterface $repository Repository
     */
    public function register($url, RepositoryInterface $repository)
    {
        // Repository registration
        $repositoryUrl = ltrim(self::normalizeRepositoryUrl($url), '/');
        $this->registry[$repositoryUrl] = $repository;
    }

    /**
     * Normalize a repository URL
     *
     * @param string|ObjectUrl $url Repository URL
     * @return string Normalized repository URL
     * @throws InvalidArgumentException If the repository URL is invalid
     */
    public static function normalizeRepositoryUrl($url)
    {
        // If it's an apparat URL
        if ($url instanceof ApparatUrl) {
            $url = $url->getNormalizedRepositoryUrl();

            // Else: If it's an object URL
        } elseif ($url instanceof ObjectUrl) {
            $url = $url->getRepositoryUrl();

            // Else: If it's an empty URL
        } elseif ($url === null) {
            return '';
        }

        // If the URL is a string
        if (is_string($url)) {
            // Strip the leading apparat base URL
            $apparatBaseUrl = getenv('APPARAT_BASE_URL');
            if (strpos($url, $apparatBaseUrl) === 0) {
                $url = strval(substr($url, strlen($apparatBaseUrl)));
            }

            // Ensure this is a bare URL (without query and fragment)
            if (Module::isAbsoluteBareUrl($apparatBaseUrl.$url)) {
                return $url;
            }
        }

        // The URL is invalid, throw an error
        throw new InvalidArgumentException(
            sprintf('Invalid repository URL "%s"', $url),
            InvalidArgumentException::INVALID_REPOSITORY_URL
        );
    }

    /**
     * Return an object repository by URL
     *
     * If a repository URL has not been pre-registered, the method tries to perform an ad-hoc registration
     * based on the URL given.
     * The repository URL may be local or remote, relative or absolute, with Apparat or HTTP scheme.
     *
     * @param string|ObjectUrl $url Repository URL
     * @return \Apparat\Object\Domain\Repository\Repository Object repository
     * @throws InvalidArgumentException If the repository URL is invalid
     * @throws InvalidArgumentException If the repository URL is unknown
     */
    public function get($url)
    {
        $url = ltrim(self::normalizeRepositoryUrl($url), '/');

        // If the repository URL is unknown
        if (!self::connected($url)) {
            throw new InvalidArgumentException(
                sprintf('Unknown repository URL "%s"', $url),
                InvalidArgumentException::UNKNOWN_REPOSITORY_URL
            );
        }

        // Return the repository instance
        return $this->registry[$url];
    }

    /**
     * Test whether a repository URL registered or can be auto-connected
     *
     * @param string $url Repository URL
     * @return bool Repository is connected
     */
    protected function connected($url)
    {
        // If the given repository URL is already registered: Success
        if (!empty($this->registry[$url])) {
            return true;
        }

        // If auto-connect is enabled: Try to auto-connect the requested repository
        return $this->autoConnectEnabled && $this->autoConnector->connect($url);
    }

    /**
     * Test whether a repository URL is registered
     *
     * @param string|ObjectUrl $url Repository URL
     * @return bool Repository URL is registered
     */
    public function isRegistered($url)
    {
        $url = ltrim(self::normalizeRepositoryUrl($url), '/');
        return array_key_exists($url, $this->registry) || self::connected($url);
    }

    /**
     * Return the adapter strategy factory
     *
     * @return AdapterStrategyFactoryInterface Adapter strategy factory
     */
    public function getAdapterStrategyFactory()
    {
        return $this->adptStrategyFactory;
    }

    /**
     * Return the object manager
     *
     * @return ManagerInterface Object manager
     */
    public function getObjectManager()
    {
        return $this->objectManager;
    }

    /*******************************************************************************
     * PRIVATE METHODS
     *******************************************************************************/

    /**
     * Enable repository auto-connections
     *
     * If the method is called without any arguments it will just return the current auto-connection state.
     *
     * @param null|boolean $autoConnect Enable / disable auto-connections
     * @return bool Status of repository auto-connection
     */
    public function useAutoConnect($autoConnect = null)
    {
        if ($autoConnect !== null) {
            $this->autoConnectEnabled = (boolean)$autoConnect;
        }
        return $this->autoConnectEnabled;
    }
}