Issues (23)

Ocrend/Kernel/Router/Router.php (1 issue)

Labels
Severity
1
<?php
2
3
/*
4
 * This file is part of the Ocrend Framewok 3 package.
5
 *
6
 * (c) Ocrend Software <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Ocrend\Kernel\Router;
13
14
use Ocrend\Kernel\Router\IRouter;
15
use Ocrend\Kernel\Router\Rules;
16
17
/**
18
 * Encargado de controlar las URL Amigables en cada controlador del sistema, es independiente al Routing de Silex.
19
 * Define por defecto 3 rutas escenciales, controlador, método e id.
20
 *
21
 * @author Brayan Narváez <[email protected]>
22
*/
23
24
final class Router implements IRouter {
25
26
    /**
27
     * Reglas definidas en Rules.php 
28
     *
29
     * @var array CONSTANTE con las reglas permitidas
30
    */
31
    const RULES = [
32
        'none', # Sin ninguna regla
33
        'letters', # Sólamente letras
34
        'alphanumeric', # Letras y números
35
        'url', # Con forma para URL (letras,números y el caracter -)
36
        'integer', # Sólamente números enteros
37
        'integer_positive', # Solamente números enteros positivos
38
        'float', # Sólamente números flotantes
39
        'float_positive' # Solamente números flotantes positivos
40
    ];
41
42
    /**
43
     * Colección de rutas existentes
44
     *
45
     * @var array 
46
    */
47
    private $routerCollection = array(
48
        '/controller' => 'home', # controlador por defecto
49
        '/method' => null, # método por defecto
50
        '/id' => null # id por defecto
51
    );
52
53
    /**
54
     * Colección de reglas para cada ruta existente
55
     *
56
     * @var array 
57
    */
58
    private $routerCollectionRules = array(
59
        '/controller' => 'letters',
60
        '/method' => 'none',
61
        '/id' => 'none'
62
    );
63
64
    /**
65
     * Petición real estructurada
66
     * 
67
     * @var array
68
    */
69
    private $real_request = array();
70
71
    /**
72
     * Uri requerida por el cliente final 
73
     * 
74
     * @var string
75
    */
76
    private $requestUri;
77
78
    /**
79
     * __construct() 
80
    */
81
    public function __construct() {
82
        global $http;
83
        
84
        # Obtener las peticiones
85
        $this->requestUri = $http->query->get('routing');
86
87
        # Verificar las peticiones
88
        $this->checkRequests();
89
    }   
90
91
    /**
92
     * Coloca una regla destinada a una ruta, siempre y cuando esta regla exista.
93
     *
94
     * @param string $index : Índice de la ruta
95
     * @param string $rule : Nombre de la regla
96
     *
97
     * @throws \RuntimeException si la regla no existe
98
     * @return void
99
    */
100
    final private function setCollectionRule(string $index, string $rule) {
101
        # Verificar si la regla existe
102
        if (!in_array($rule, self::RULES)) {
103
            throw new \RuntimeException('La regla ' . $rule . ' no existe.');
104
        }
105
        # Definir la regla para la ruta
106
        $this->routerCollectionRules[$index] = $rule;
107
    }
108
109
    /**
110
     * Verifica las peticiones por defecto
111
    */
112
    final private function checkRequests()  {
113
        # Verificar si existe peticiones
114
        if (null !== $this->requestUri) {
115
            $this->real_request = explode('/', $this->requestUri);
116
            $this->routerCollection['/controller'] = $this->real_request[0];
117
        }
118
119
        # Setear las siguientes rutas
120
        $this->routerCollection['/method'] = array_key_exists(1, $this->real_request) ? $this->real_request[1] : null;
121
        $this->routerCollection['/id'] = array_key_exists(2, $this->real_request) ? $this->real_request[2] : null;
122
    }
123
124
    /**
125
     * Crea una nueva ruta.
126
     *
127
     * @param string $index : Índice de la ruta
128
     * @param string $rule : Nombre de la regla, por defecto es ninguna "none"
129
     *
130
     * @throws \RuntimeException si no puede definirse la ruta
131
    */
132
    final public function setRoute(string $index, string $rule = 'none')  {
133
        # Nombres de rutas no permitidos
134
        if (in_array($index, ['/controller', '/method', '/id'])) {
135
            throw new \RuntimeException('No puede definirse ' . $index . ' como índice en la ruta.');
136
        }
137
138
        # Sobreescribir
139
        unset(
140
            $this->routerCollection[$index],
141
            $this->routerCollectionRules[$index]
142
        );
143
            
144
        # Definir la ruta y regla
145
        $lastRoute = sizeof($this->routerCollection);
146
        $this->routerCollection[$index] = array_key_exists($lastRoute, $this->real_request) ? $this->real_request[$lastRoute] : null;
147
        $this->setCollectionRule($index, $rule);
148
    }
149
    
150
    /**
151
     * Obtiene el valor de una ruta según la regla que ha sido definida y si ésta existe.
152
     *
153
     * @param string $index : Índice de la ruta
154
     *
155
     * @throws \RuntimeException si la ruta no existe o si no está implementada la regla
156
     * @return mixed : Valor de la ruta solicitada
157
     */
158
    final public function getRoute(string $index) {
159
          # Verificar existencia de ruta
160
        if (!array_key_exists($index, $this->routerCollection)) {
161
            throw new \RuntimeException('La ruta ' . $index . ' no está definida en el controlador.');
162
        }
163
164
        # Obtener la ruta nativa sin reglas
165
        $ruta = $this->routerCollection[$index];
166
        $rules = new Rules;
167
168
        # Retornar ruta con la regla definida aplicada
169
        if (method_exists($rules, $this->routerCollectionRules[$index])) {
170
            return $rules->{$this->routerCollectionRules[$index]}($ruta);
171
        } 
172
173
        # No existe la regla solicitada
174
        throw new \RuntimeException('La regla ' . $this->routerCollectionRules[$index] . ' existe en RULES pero no está implementada.');    
175
    }
176
177
    /**
178
     * Obtiene el nombre del controlador.
179
     * 
180
     * @return string controlador.
181
     */    
182
    final public function getController() {
183
        return $this->routerCollection['/controller'];
184
    }
185
186
    /**
187
     * Obtiene el método
188
     * 
189
     * @return string con el método.
190
     *           null si no está definido.
191
     */
192
    final public function getMethod() {
193
        return $this->routerCollection['/method'];
194
    }   
195
196
    /**
197
     * Obtiene el id
198
     *
199
     * @param bool $with_rules : true para obtener el id con reglas definidas para números mayores a 0
200
     *                           false para obtener el id sin reglas definidas
201
     * 
202
     * @return int|null con el id
203
     *           int con el id si usa reglas.
204
     *           null si no está definido.
205
     */
206
    final public function getId(bool $with_rules = false) {
207
        $id = $this->routerCollection['/id'];
208
        if ($with_rules && (!is_numeric($id) || $id <= 0)) {
209
            return null;
210
        }
211
212
        return $id;
213
    }   
214
215
    /**
216
     * Encargado de cargar un controlador
217
     * Si este no existe, ejecutará errorController.
218
     * Si no se solicita ningún controlador, ejecutará homeController.
219
     * 
220
     * @return void
221
     */
222
    final private function loadController()  {
223
        # Definir controlador
224
        if (null != ($controller = $this->getController())) {
225
            $controller = $controller . 'Controller';
226
227
            if (!is_readable('app/controllers/' . $controller . '.php')) {
228
                $controller = 'errorController';
229
            }
230
231
        } else {
232
            $controller = 'errorController';
233
        }  
234
235
        $controller = 'app\\controllers\\' . $controller;    
236
237
        new $controller($this);
238
    }
239
240
    /**
241
     * Error a mostrar en producción
242
     * 
243
     * @return void
244
     */
245
    final private function productionError() {
246
        global $http;
247
248
        header($http->server->get('SERVER_PROTOCOL') . ' 500 Internal Server Error', true, 500);
249
        header('Content-Type: text/html; charset=utf-8');
250
        header('Content-language: es');
251
252
        $output = file_get_contents('assets/error/catch.html', FILE_USE_INCLUDE_PATH);
0 ignored issues
show
Ocrend\Kernel\Router\FILE_USE_INCLUDE_PATH of type integer is incompatible with the type boolean expected by parameter $use_include_path of file_get_contents(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

252
        $output = file_get_contents('assets/error/catch.html', /** @scrutinizer ignore-type */ FILE_USE_INCLUDE_PATH);
Loading history...
253
        echo $output;
254
    }
255
256
    /**
257
     * Ejecuta el controlador solicitado por la URL.
258
     * 
259
     * @return void
260
     */
261
    final public function executeController()  {
262
        global $config;
263
264
        if($config['build']['production']) {
265
            try {
266
                $this->loadController();
267
            } catch(\Throwable $e) {
268
                $this->productionError();
269
            } catch(\Exception $e) {
270
                $this->productionError();
271
            }
272
        } else {
273
            $this->loadController();
274
        }
275
    }
276
277
}