ICanBoogie/Module 4.0.x
  • Namespace
  • Class

Namespaces

  • ICanBoogie
    • Module
      • ModuleCollection
      • Operation

Classes

  • Descriptor
  • ForwardedOperationDispatcher
  • Hooks
  • ModelCollection
  • ModuleCollection
  • ModuleOperationDispatcher
  • ModuleRouteDefinition
  • ModuleTemplateResolver

Traits

  • ApplicationBindings
  • ControllerBindings
  • ModuleRoute

Exceptions

  • ModuleCollectionInstallFailed
  • ModuleConstructorMissing
  • ModuleIsDisabled
  • ModuleNotDefined
  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 
<?php

/*
 * This file is part of the ICanBoogie package.
 *
 * (c) Olivier Laviale <olivier.laviale@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ICanBoogie\Module;

use ICanBoogie\HTTP\Dispatcher;
use ICanBoogie\HTTP\Request;
use ICanBoogie\Module;
use ICanBoogie\Operation;

use function ICanBoogie\camelize;
use function ICanBoogie\log_error;
use function ICanBoogie\format;

/**
 * A dispatcher for forwarded operations.
 */
class ForwardedOperationDispatcher implements Dispatcher
{
    /**
     * Formats the specified namespace and operation name into an operation class.
     *
     * @param string $operation_name
     *
     * @return string
     */
    static private function format_class_name($operation_name)
    {
        return camelize(strtr($operation_name, '-', '_')) . 'Operation';
    }

    /**
     * @var ModuleCollection
     */
    private $modules;

    /**
     * @param ModuleCollection $modules
     */
    public function __construct(ModuleCollection $modules)
    {
        $this->modules = $modules;
    }

    /**
     * @inheritdoc
     *
     * @return Operation\Response
     */
    public function __invoke(Request $request)
    {
        if (!$request[Operation::DESTINATION] && !$request[Operation::NAME])
        {
            return null;
        }

        $this->assert_forward_is_valid($request);

        $operation = $this->resolve_operation($request);
        $response = $operation($request);

        return $request->is_xhr || $response->location ? $response : null;
    }

    /**
     * If the exception in not a {@link Failure} instance it is rethrown, otherwise it means
     * that an exception occurred during control/validate/process or a Failure was thrown
     * because the response has an error.
     *
     * - If the request is an XHR we return the response of the operation.
     *
     * - Otherwise, the exception message is logged as an error and no response is returned
     * so that the actual content of the URL is displayed.
     *
     * @inheritdoc
     */
    public function rescue(\Exception $exception, Request $request)
    {
        if (!$exception instanceof Operation\Failure)
        {
            throw $exception;
        }

        if ($request->is_xhr)
        {
            return $exception->operation->response;
        }

        $response = null;

        new Operation\RescueEvent($exception->operation, $exception, $request, $response);

        if ($response)
        {
            return $response;
        }

        $previous = $exception->previous;

        if ($previous)
        {
            log_error($previous->getMessage());
        }

        return null;
    }

    /**
     * Asserts that a forward is valid.
     *
     * @param Request $request
     *
     * @throws \LogicException if {@link Operation::DESTINATION} or {@link Operation::NAME} is
     * not defined.
     */
    protected function assert_forward_is_valid(Request $request)
    {
        if (!$request[Operation::DESTINATION])
        {
            throw new \LogicException("The operation's destination is required.");
        }

        if (!$request[Operation::NAME])
        {
            throw new \LogicException("The operation's name is required.");
        }
    }

    /**
     * Resolves an {@link Operation} instance from forwarded parameters.
     *
     * @param Request $request
     *
     * @return Operation
     */
    protected function resolve_operation(Request $request)
    {
        $module_id = $request[Operation::DESTINATION];
        $module = $this->modules[$module_id];
        $operation_name = $request[Operation::NAME];
        $operation_key = $request[Operation::KEY];
        $constructor = $this->resolve_operation_constructor($operation_name, $module);

        return $constructor([

            'key' => $operation_key,
            'module' => $module

        ]);
    }

    /**
     * Resolves operation constructor.
     *
     * @param string $operation_name
     * @param Module $module
     *
     * @return callable
     *
     * @throws \LogicException If the {@link Operation} instance cannot be resolved.
     */
    protected function resolve_operation_constructor($operation_name, Module $module)
    {
        $operation_class = $this->modules->resolve_classname('Operation\\' . self::format_class_name($operation_name), $module);

        if (!$operation_class)
        {
            throw new \LogicException(format
            (
                'The operation %operation is not supported by the module %module.', [

                    '%module' => $module->id,
                    '%operation' => $operation_name
                ]
            ), 404);
        }

        return [ $operation_class, 'from' ];
    }
}
ICanBoogie/Module 4.0.x API documentation generated by ApiGen