Completed
Push — 1.0 ( 6afa02...1214b5 )
by joanhey
02:03
created

KumbiaRest   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 302
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 0
loc 302
rs 9
c 0
b 0
f 0
wmc 35
lcom 1
cbo 4

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A initREST() 0 8 1
B rewriteActionName() 0 22 5
A actionExist() 0 8 2
A param() 0 14 4
A error() 0 6 1
A accept() 0 19 3
A parseJSON() 0 9 3
A parseXML() 0 12 3
A parseCSV() 0 13 2
A parseForm() 0 6 1
A getInputFormat() 0 10 2
A getOutputFormat() 0 12 3
A getHeaders() 0 17 4
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.txt.
9
 * It is also available through the world-wide-web at this URL:
10
 * http://wiki.kumbiaphp.com/Licencia
11
 * If you did not receive a copy of the license and are unable to
12
 * obtain it through the world-wide-web, please send an email
13
 * to [email protected] so we can send you a copy immediately.
14
 *
15
 * @category   Kumbia
16
 *
17
 * @copyright  Copyright (c) 2005 - 2017 Kumbia Team (http://www.kumbiaphp.com)
18
 * @license    http://wiki.kumbiaphp.com/Licencia     New BSD License
19
 */
20
require_once __DIR__.'/controller.php';
21
22
/**
23
 * Controlador para manejar peticiones REST.
24
 *
25
 * Por defecto cada acción se llama como el método usado por el cliente
26
 * (GET, POST, PUT, DELETE, OPTIONS, HEADERS, PURGE...)
27
 * ademas se puede añadir mas acciones colocando delante el nombre del método
28
 * seguido del nombre de la acción put_cancel, post_reset...
29
 *
30
 * @category Kumbia
31
 *
32
 * @author kumbiaPHP Team
33
 */
34
class KumbiaRest extends Controller
35
{
36
    /**
37
     * Formato de entrada usado para interpretar los datos
38
     * enviados por el cliente.
39
     *
40
     * @var string MIME Type del formato
41
     */
42
    protected $_fInput = null;
43
44
    /**
45
     * Permite definir parser personalizados por MIME TYPE
46
     * Esto es necesario para interpretar las entradas
47
     * Se define como un MIME type como clave y el valor debe ser un
48
     * callback que devuelva los datos interpretado.
49
     */
50
    protected $_inputType = array(
51
        'application/json' => array('RestController', 'parseJSON'),
52
        'application/xml' => array('RestController', 'parseXML'),
53
        'text/xml' => array('RestController', 'parseXML'),
54
        'text/csv' => array('RestController', 'parseCSV'),
55
        'application/x-www-form-urlencoded' => array('RestController', 'parseForm'),
56
    );
57
58
    /**
59
     * Formato de salida enviada al cliente.
60
     *
61
     * @var string nombre del template a usar
62
     */
63
    protected $_fOutput = null;
64
65
    /**
66
     * Permite definir las salidas disponibles,
67
     * de esta manera se puede presentar la misma salida en distintos
68
     * formatos a requerimientos del cliente.
69
     */
70
    protected $_outputType = array(
71
        'application/json' => 'json',
72
        'application/xml' => 'xml',
73
        'text/xml' => 'xml',
74
        'text/csv' => 'csv',
75
    );
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 tendra será el metodo de
103
         * la peticion: 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 ($action === 'index' && $method !== 'post') {
114
            $this->action_name = 'getAll';
115
116
            return;
117
        }
118
        $this->action_name = $method;
119
        $this->parameters = ($action === 'index') ? $this->parameters : array($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 parametros 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 proridad es uno, 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
        if (function_exists('json_decode')) {
209
            $result = json_decode($input, true);
210
            if ($result) {
211
                return $result;
212
            }
213
        }
214
    }
215
216
    /**
217
     * Parse XML.
218
     *
219
     * Convierte formato XML en un objeto, esto será necesario volverlo estandar
220
     * si se devuelven objetos o arrays asociativos
221
     *
222
     * @param string $input
223
     *
224
     * @return \SimpleXMLElement|string
225
     */
226
    protected static function parseXML($input)
227
    {
228
        if (class_exists('SimpleXMLElement')) {
229
            try {
230
                return new SimpleXMLElement($input);
231
            } catch (Exception $e) {
232
                // Do nothing
233
            }
234
        }
235
236
        return $input;
237
    }
238
239
    /**
240
     * Parse CSV.
241
     *
242
     * Convierte CSV en arrays numéricos,
243
     * cada item es una linea
244
     *
245
     * @param string $input
246
     *
247
     * @return array
248
     */
249
    protected static function parseCSV($input)
250
    {
251
        $temp = fopen('php://memory', 'rw');
252
        fwrite($temp, $input);
253
        fseek($temp, 0);
254
        $res = array();
255
        while (($data = fgetcsv($temp)) !== false) {
256
            $res[] = $data;
257
        }
258
        fclose($temp);
259
260
        return $res;
261
    }
262
263
    /**
264
     * Realiza la conversion de formato de Formulario a array.
265
     *
266
     * @param string $input
267
     *
268
     * @return arrat
269
     */
270
    protected static function parseForm($input)
271
    {
272
        parse_str($input, $vars);
273
274
        return $vars;
275
    }
276
277
    /**
278
     * Retorna el tipo de formato de entrada.
279
     *
280
     * @return string
281
     */
282
    protected static function getInputFormat()
283
    {
284
        $str = '';
285
        if (isset($_SERVER['CONTENT_TYPE'])) {
286
            $s = explode(';', $_SERVER['CONTENT_TYPE']);
287
            $str = trim($s[0]);
288
        }
289
290
        return $str;
291
    }
292
293
    /**
294
     * Devuelve le nombre del formato de salida.
295
     *
296
     * @param array $validOutput Array de formatos de salida soportado
297
     *
298
     * @return string
299
     */
300
    protected function getOutputFormat(array $validOutput)
301
    {
302
        /* busco un posible formato de salida */
303
        $accept = self::accept();
304
        foreach ($accept as $key => $a) {
305
            if (array_key_exists($key, $validOutput)) {
306
                return $validOutput[$key];
307
            }
308
        }
309
310
        return 'json';
311
    }
312
313
    /**
314
     * Retorna todas las cabeceras enviadas por el cliente.
315
     *
316
     * @return array
317
     */
318
    protected static function getHeaders()
319
    {
320
        /*Esta función solo existe en apache*/
321
        if (function_exists('getallheaders')) {
322
            return getallheaders();
323
        }
324
325
        $headers = array();
326
327
        foreach ($_SERVER as $name => $value) {
328
            if (substr($name, 0, 5) == 'HTTP_') {
329
                $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
330
            }
331
        }
332
333
        return $headers;
334
    }
335
}
336