KumbiaRest   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 284
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 0
loc 284
rs 10
c 0
b 0
f 0
wmc 28
lcom 1
cbo 4

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A initREST() 0 8 1
A actionExist() 0 8 2
A param() 0 14 4
A error() 0 6 1
A accept() 0 19 3
A rewriteActionName() 0 22 4
A parseJSON() 0 4 1
A parseXML() 0 8 2
A parseCSV() 0 13 2
A parseForm() 0 6 1
A getInputFormat() 0 9 2
A getOutputFormat() 0 12 3
A getHeaders() 0 4 1
1
<?php
2
/**
3
 * KumbiaPHP web & app Framework
4
 *
5
 * LICENSE
6
 *
7
 * This source file is subject to the new BSD license that is bundled
8
 * with this package in the file LICENSE.
9
 *
10
 * @category   Kumbia
11
 *
12
 * @copyright  Copyright (c) 2005 - 2019 KumbiaPHP Team (http://www.kumbiaphp.com)
13
 * @license    https://github.com/KumbiaPHP/KumbiaPHP/blob/master/LICENSE   New BSD License
14
 */
15
require_once __DIR__.'/controller.php';
16
17
/**
18
 * Controlador para manejar peticiones REST.
19
 *
20
 * Por defecto cada acción se llama como el método usado por el cliente
21
 * (GET, POST, PUT, DELETE, OPTIONS, HEADERS, PURGE...)
22
 * ademas se puede añadir mas acciones colocando delante el nombre del método
23
 * seguido del nombre de la acción put_cancel, post_reset...
24
 *
25
 * @category Kumbia
26
 *
27
 * @author kumbiaPHP Team
28
 */
29
class KumbiaRest extends Controller
30
{
31
    /**
32
     * Formato de entrada usado para interpretar los datos
33
     * enviados por el cliente.
34
     *
35
     * @var string MIME Type del formato
36
     */
37
    protected $_fInput;
38
39
    /**
40
     * Permite definir parser personalizados por MIME TYPE
41
     * Esto es necesario para interpretar las entradas
42
     * Se define como un MIME type como clave y el valor debe ser un
43
     * callback que devuelva los datos interpretado.
44
     */
45
    protected $_inputType = array(
46
        'application/json' => array('RestController', 'parseJSON'),
47
        'application/xml' => array('RestController', 'parseXML'),
48
        'text/xml' => array('RestController', 'parseXML'),
49
        'text/csv' => array('RestController', 'parseCSV'),
50
        'application/x-www-form-urlencoded' => array('RestController', 'parseForm'),
51
    );
52
53
    /**
54
     * Formato de salida enviada al cliente.
55
     *
56
     * @var string nombre del template a usar
57
     */
58
    protected $_fOutput;
59
60
    /**
61
     * Permite definir las salidas disponibles,
62
     * de esta manera se puede presentar la misma salida en distintos
63
     * formatos a requerimientos del cliente.
64
     */
65
    protected $_outputType = array(
66
        'application/json' => 'json',
67
        'application/xml' => 'xml',
68
        'text/xml' => 'xml',
69
        'text/csv' => 'csv',
70
    );
71
72
    /**
73
     * Constructor
74
     *
75
     * @param array $arg
76
     */
77
    public function __construct($arg)
78
    {
79
        parent::__construct($arg);
80
        $this->initREST();
81
    }
82
83
    /**
84
     * Hacer el router de la petición y envia los parametros correspondientes
85
     * a la acción, adema captura formatos de entrada y salida. 
86
     */
87
    protected function initREST()
88
    {
89
        /* formato de entrada */
90
        $this->_fInput = self::getInputFormat();
91
        $this->_fOutput = self::getOutputFormat($this->_outputType);
92
        View::select(null, $this->_fOutput);
93
        $this->rewriteActionName();
94
    }
95
96
    /**
97
     * Reescribe la acción.
98
     */
99
    protected function rewriteActionName()
100
    {
101
        /**
102
         * reescribimos la acción a ejecutar, ahora será el método de
103
         * la petición: get(:id), getAll , put, post, delete, etc.
104
         */
105
        $action = $this->action_name;
106
        $method = strtolower(Router::get('method'));
107
        $rewrite = "{$method}_{$action}";
108
        if ($this->actionExist($rewrite)) {
109
            $this->action_name = $rewrite;
110
111
            return;
112
        }
113
        if ($rewrite === 'get_index') {
114
            $this->action_name = 'getAll';
115
116
            return;
117
        }
118
        $this->action_name = $method;
119
        $this->parameters = ($action === 'index') ? $this->parameters : [$action] + $this->parameters;
120
    }
121
122
    /**
123
     * Verifica si existe la acción $name existe.
124
     *
125
     * @param string $name nombre de la acción
126
     *
127
     * @return bool
128
     */
129
    protected function actionExist($name)
130
    {
131
        if (method_exists($this, $name)) {
132
            return (new ReflectionMethod($this, $name))->isPublic();
133
        }
134
135
        return false;
136
    }
137
138
    /**
139
     * Retorna los parámetros de la petición el función del formato de entrada
140
     * de los mismos. Hace uso de los parser definidos en la clase.
141
     */
142
    protected function param()
143
    {
144
        $input = file_get_contents('php://input');
145
        $format = $this->_fInput;
146
        /* verifica si el formato tiene un parser válido */
147
        if (isset($this->_inputType[$format]) && is_callable($this->_inputType[$format])) {
148
            $result = call_user_func($this->_inputType[$format], $input);
149
            if ($result) {
150
                return $result;
151
            }
152
        }
153
154
        return $input;
155
    }
156
157
    /**
158
     * Envia un error al cliente junto con el mensaje.
159
     *
160
     * @param string $text  texto del error
161
     * @param int    $error Número del error HTTP
162
     *
163
     * @return array data de error
164
     */
165
    protected function error($text, $error = 400)
166
    {
167
        http_response_code((int) $error);
168
169
        return array('error' => $text);
170
    }
171
172
    /**
173
     * Retorna los formato aceptados por el cliente ordenados por prioridad
174
     * interpretando la cabecera HTTP_ACCEPT.
175
     *
176
     * @return array
177
     */
178
    protected static function accept()
179
    {
180
        /* para almacenar los valores acceptados por el cliente */
181
        $aTypes = array();
182
        /* Elimina espacios, convierte a minusculas, y separa */
183
        $accept = explode(',', strtolower(str_replace(' ', '', Input::server('HTTP_ACCEPT'))));
184
        foreach ($accept as $a) {
185
            $q = 1; /* Por defecto la prioridad es 1, el siguiente verifica si es otra */
186
            if (strpos($a, ';q=')) {
187
                /* parte el "mime/type;q=X" en dos: "mime/type" y "X" */
188
                list($a, $q) = explode(';q=', $a);
189
            }
190
            $aTypes[$a] = $q;
191
        }
192
        /* ordena por prioridad (mayor a menor) */
193
        arsort($aTypes);
194
195
        return $aTypes;
196
    }
197
198
    /**
199
     * Parse JSON
200
     * Convierte formato JSON en array asociativo.
201
     *
202
     * @param string $input
203
     *
204
     * @return array|string
205
     */
206
    protected static function parseJSON($input)
207
    {
208
        return json_decode($input, true);
209
    }
210
211
    /**
212
     * Parse XML.
213
     *
214
     * Convierte formato XML en un objeto, esto será necesario volverlo estandar
215
     * si se devuelven objetos o arrays asociativos
216
     *
217
     * @param string $input
218
     *
219
     * @return \SimpleXMLElement|null
220
     */
221
    protected static function parseXML($input)
222
    {    
223
        try {
224
            return new SimpleXMLElement($input);
225
        } catch (Exception $e) {
226
            // Do nothing
227
        }
228
    }
229
230
    /**
231
     * Parse CSV.
232
     *
233
     * Convierte CSV en arrays numéricos,
234
     * cada item es una linea
235
     *
236
     * @param string $input
237
     *
238
     * @return array
239
     */
240
    protected static function parseCSV($input)
241
    {
242
        $temp = fopen('php://memory', 'rw');
243
        fwrite($temp, $input);
244
        fseek($temp, 0);
245
        $res = array();
246
        while (($data = fgetcsv($temp)) !== false) {
247
            $res[] = $data;
248
        }
249
        fclose($temp);
250
251
        return $res;
252
    }
253
254
    /**
255
     * Realiza la conversión de formato de Formulario a array.
256
     *
257
     * @param string $input
258
     *
259
     * @return array
260
     */
261
    protected static function parseForm($input)
262
    {
263
        parse_str($input, $vars);
264
265
        return $vars;
266
    }
267
268
    /**
269
     * Retorna el tipo de formato de entrada.
270
     *
271
     * @return string
272
     */
273
    protected static function getInputFormat()
274
    {
275
        if (isset($_SERVER['CONTENT_TYPE'])) {
276
            $str = explode(';', $_SERVER['CONTENT_TYPE']);
277
            return trim($str[0]);
278
        }
279
280
        return '';
281
    }
282
283
    /**
284
     * Devuelve le nombre del formato de salida.
285
     *
286
     * @param array $validOutput Array de formatos de salida soportado
287
     *
288
     * @return string
289
     */
290
    protected function getOutputFormat(array $validOutput)
291
    {
292
        /* busco un posible formato de salida */
293
        $accept = self::accept();
294
        foreach ($accept as $key) {
295
            if (array_key_exists($key, $validOutput)) {
296
                return $validOutput[$key];
297
            }
298
        }
299
300
        return 'json';
301
    }
302
303
    /**
304
     * Retorna todas las cabeceras enviadas por el cliente.
305
     *
306
     * @return array
307
     */
308
    protected static function getHeaders()
309
    {
310
        return getallheaders();
311
    }
312
}
313