apparat/object

View on GitHub
src/Object/Module.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

/**
 * apparat-object
 *
 * @category    Apparat
 * @package     Apparat\Object
 * @subpackage  Apparat\Object
 * @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;

use Apparat\Kernel\Ports\AbstractModule;
use Apparat\Kernel\Ports\Contract\DependencyInjectionContainerInterface;
use Apparat\Object\Application\Contract\BinaryPayloadProcessorInterface;
use Apparat\Object\Application\Contract\CommonMarkPayloadProcessorInterface;
use Apparat\Object\Application\Model\Object\AbstractBinaryObject;
use Apparat\Object\Application\Model\Object\AbstractCommonMarkObject;
use Apparat\Object\Application\Model\Object\Manager;
use Apparat\Object\Application\Service\TypeService;
use Apparat\Object\Domain\Contract\TypeServiceInterface;
use Apparat\Object\Domain\Model\Object\ManagerInterface;
use Apparat\Object\Domain\Model\Object\Type;
use Apparat\Object\Domain\Model\Properties\MetaProperties;
use Apparat\Object\Domain\Repository\AdapterStrategyFactoryInterface;
use Apparat\Object\Domain\Repository\AutoConnectorInterface;
use Apparat\Object\Domain\Repository\Selector;
use Apparat\Object\Domain\Repository\Service;
use Apparat\Object\Infrastructure\Factory\AdapterStrategyFactory;
use Apparat\Object\Infrastructure\Repository\AutoConnector;
use Apparat\Object\Infrastructure\Utilities\BinaryPayloadProcessor;
use Apparat\Object\Infrastructure\Utilities\CommonMarkPayloadProcessor;
use Apparat\Object\Ports\Types\Object;
use Dotenv\Dotenv;

/**
 * Object module
 *
 * @package Apparat\Object
 * @subpackage Apparat\Object
 */
class Module extends AbstractModule
{
    /**
     * Module name
     *
     * @var string
     */
    const NAME = 'object';

    /**
     * Validate the environment
     *
     * @param Dotenv $environment Environment
     */
    protected static function validateEnvironment(Dotenv $environment)
    {
        parent::validateEnvironment($environment);

        // Validate the required environment variables
        $environment->required('APPARAT_BASE_URL')->notEmpty();
        $environment->required('OBJECT_RESOURCE_EXTENSION')->notEmpty();
        $environment->required('OBJECT_DATE_PRECISION')->isInteger()->allowedValues([0, 1, 2, 3, 4, 5, 6]);
        $environment->required('OBJECT_DEFAULT_PRIVACY')->notEmpty()->allowedValues(MetaProperties::$privacyLevels);
        $environment->required('OBJECT_DEFAULT_LANGUAGE')->notEmpty();
        $environment->required('OBJECT_ENABLE_TYPES')->notEmpty();

        // In-depth validation of the apparat base URL
        $apparatBaseUrl = getenv('APPARAT_BASE_URL');
        self::isAbsoluteBareUrl($apparatBaseUrl);

        // Normalize the apparat base URL
        putenv('APPARAT_BASE_URL='.rtrim($apparatBaseUrl, '/').'/');

        // Enable the configured object types
        array_map([Object::class, 'enableType'], explode(',', getenv('OBJECT_ENABLE_TYPES')));
    }

    /**
     * Test whether a URL is absolute and does not have query parameters and / or a fragment
     *
     * @param string $url URL
     * @return boolean If the URL is absolute and has neither query parameters or a fragment
     * @throws \RuntimeException If the URL is not absolute / valid
     * @throws \RuntimeException If the URL has query parameters
     * @throws \RuntimeException If the URL has a fragment
     */
    public static function isAbsoluteBareUrl($url)
    {
        if (!filter_var($url) || !preg_match("%^https?\:\/\/%i", $url)) {
            throw new \RuntimeException(sprintf('Apparat base URL "%s" must be valid', $url), 1451776352);
        }
        if (strlen(parse_url($url, PHP_URL_QUERY))) {
            throw new \RuntimeException(
                sprintf('Apparat base URL "%s" must not contain query parameters', $url),
                1451776509
            );
        }
        if (strlen(parse_url($url, PHP_URL_FRAGMENT))) {
            throw new \RuntimeException(sprintf('Apparat base URL "%s" must not contain a fragment', $url), 1451776570);
        }

        return true;
    }

    /**
     * Configure the dependency injection container
     *
     * @param DependencyInjectionContainerInterface $diContainer Dependency injection container
     * @return void
     */
    public function configureDependencyInjection(DependencyInjectionContainerInterface $diContainer)
    {
        parent::configureDependencyInjection($diContainer);

        // Configure the repository service
        $diContainer->register(Service::class, [
            'shared' => true,
            'substitutions' => [
                AutoConnectorInterface::class => [
                    'instance' => AutoConnector::class,
                ],
                AdapterStrategyFactoryInterface::class => [
                    'instance' => AdapterStrategyFactory::class,
                ],
                ManagerInterface::class => [
                    'instance' => Manager::class,
                ],
            ]
        ]);

        // Configure the type service
        $diContainer->register(Type::class, [
            'substitutions' => [
                TypeServiceInterface::class => [
                    'instance' => TypeService::class,
                ],
            ]
        ]);
        $diContainer->register(Selector::class, [
            'substitutions' => [
                TypeServiceInterface::class => [
                    'instance' => TypeService::class,
                ],
            ]
        ]);

        // Configure the CommonMark payload processor
        $diContainer->register(AbstractCommonMarkObject::class, [
            'shared' => false,
            'substitutions' => [
                CommonMarkPayloadProcessorInterface::class => [
                    'instance' => CommonMarkPayloadProcessor::class,
                ],
            ]
        ]);

        // Configure the binary payload processor
        $diContainer->register(AbstractBinaryObject::class, [
            'shared' => false,
            'substitutions' => [
                BinaryPayloadProcessorInterface::class => [
                    'instance' => BinaryPayloadProcessor::class,
                ],
            ]
        ]);
    }
}