Passed
Push — 1.0.0-dev ( 9f70ff...e509c6 )
by nguereza
02:49
created

autoload_controller()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
    defined('ROOT_PATH') || exit('Access denied');
3
    /**
4
     * TNH Framework
5
     *
6
     * A simple PHP framework using HMVC architecture
7
     *
8
     * This content is released under the MIT License (MIT)
9
     *
10
     * Copyright (c) 2017 TNH Framework
11
     *
12
     * Permission is hereby granted, free of charge, to any person obtaining a copy
13
     * of this software and associated documentation files (the "Software"), to deal
14
     * in the Software without restriction, including without limitation the rights
15
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
     * copies of the Software, and to permit persons to whom the Software is
17
     * furnished to do so, subject to the following conditions:
18
     *
19
     * The above copyright notice and this permission notice shall be included in all
20
     * copies or substantial portions of the Software.
21
     *
22
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
     * SOFTWARE.
29
     */
30
31
    /**
32
     *  @file common.php
33
     *  
34
     *  Contains most of the commons functions used by the system
35
     *  
36
     *  @package	core
37
     *  @author	TNH Framework team
38
     *  @copyright	Copyright (c) 2017
39
     *  @license	http://opensource.org/licenses/MIT	MIT License
40
     *  @link	http://www.iacademy.cf
41
     *  @version 1.0.0
42
     *  @filesource
43
     */
44
	
45
46
    /**
47
     * This function is the class loader helper is used if the library "Loader" not yet loaded
48
     * he load the class once
49
     * @param  string $class  the class name to be loaded
50
     * @param  string $dir    the directory where to find the class
51
     * @param  mixed $params the parameter to pass as argument to the constructor of the class
52
     * @codeCoverageIgnore
53
     * 
54
     * @return object         the instance of the loaded class
55
     */
56
    function & class_loader($class, $dir = 'libraries', $params = null){
57
        //put the first letter of class to upper case 
58
        $class = ucfirst($class);
59
        static $classes = array();
60
        if (isset($classes[$class]) /*hack for duplicate log Logger name*/ && $class != 'Log') {
61
            return $classes[$class];
62
        }
63
        $found = false;
64
        foreach (array(APPS_PATH, CORE_PATH) as $path) {
65
            $file = $path . $dir . DS . $class . '.php';
66
            if (file_exists($file)) {
67
                if (class_exists($class, false) === false) {
68
                    require_once $file;
69
                }
70
                //already found
71
                $found = true;
72
                break;
73
            }
74
        }
75
        if (!$found) {
76
            //can't use show_error() at this time because some dependencies not yet loaded
77
            set_http_status_header(503);
78
            echo 'Cannot find the class [' . $class . ']';
79
            die();
80
        }
81
		
82
        /*
83
		   TODO use the best method to get the Log instance
84
		 */
85
        if ($class == 'Log') {
86
            //can't use the instruction like "return new Log()" 
87
            //because we need return the reference instance of the loaded class.
88
            $log = new Log();
89
            return $log;
90
        }
91
        //track of loaded classes
92
        class_loaded($class);
93
		
94
        //record the class instance
95
        $classes[$class] = isset($params) ? new $class($params) : new $class();
96
		
97
        return $classes[$class];
98
    }
99
100
    /**
101
     * This function is the helper to record the loaded classes
102
     * @param  string $class the loaded class name
103
     * @codeCoverageIgnore
104
     * 
105
     * @return array        the list of the loaded classes
106
     */
107
    function & class_loaded($class = null){
108
        static $list = array();
109
        if ($class !== null) {
110
            $list[strtolower($class)] = $class;
111
        }
112
        return $list;
113
    }
114
115
    /**
116
     * This function is used to load the configurations in the case the "Config" library not yet loaded
117
     * @param  array  $overwrite_values if need overwrite the existing configuration
118
     * @codeCoverageIgnore
119
     * 
120
     * @return array                   the configurations values
121
     */
122
    function & load_configurations(array $overwrite_values = array()){
123
        static $config;
124
        if (empty($config)) {
125
            $file = CONFIG_PATH . 'config.php';
126
            $found = false;
127
            if (file_exists($file)) {
128
                require_once $file;
129
                $found = true;
130
            }
131
            if (!$found) {
132
                set_http_status_header(503);
133
                echo 'Unable to find the configuration file [' . $file . ']';
134
                die();
135
            }
136
        }
137
        foreach ($overwrite_values as $key => $value) {
138
            $config[$key] = $value;
139
        }
140
        return $config;
141
    }
142
143
    /**
144
     * This function is the helper to get the config value in case the "Config" library not yet loaded
145
     * @param  string $key     the config item to get the vale
146
     * @param  mixed $default the default value to return if can't find the config item in the configuration
147
     * @test
148
     * 
149
     * @return mixed          the config value
150
     */
151
    function get_config($key, $default = null) {
152
        static $cfg;
153
        if (empty($cfg)) {
154
            $cfg[0] = & load_configurations();
155
            if(! is_array($cfg[0])){
156
                $cfg[0] = array();
157
            }
158
        }
159
        return array_key_exists($key, $cfg[0]) ? $cfg[0][$key] : $default;
160
    }
161
162
    /**
163
     * This function is a helper to logging message
164
     * @param  string $level   the log level "ERROR", "DEBUG", "INFO", etc.
165
     * @param  string $message the log message to be saved
166
     * @param  string $logger  the logger to use if is set
167
     * 
168
     * @codeCoverageIgnore
169
     */
170
    function save_to_log($level, $message, $logger = null) {
171
        $log = & class_loader('Log', 'classes');
172
        if ($logger) {
173
            $log->setLogger($logger);
174
        }
175
        $log->writeLog($message, $level);
176
    }
177
178
    /**
179
     * Set the HTTP status header
180
     * @param integer $code the HTTP status code
181
     * @param string  $text the HTTP status text
182
     * 
183
     * @codeCoverageIgnore
184
     */
185
    function set_http_status_header($code = 200, $text = null) {
186
        if (empty($text)) {
187
            $http_status = array(
188
                                100 => 'Continue',
189
                                101 => 'Switching Protocols',
190
                                200 => 'OK',
191
                                201 => 'Created',
192
                                202 => 'Accepted',
193
                                203 => 'Non-Authoritative Information',
194
                                204 => 'No Content',
195
                                205 => 'Reset Content',
196
                                206 => 'Partial Content',
197
                                300 => 'Multiple Choices',
198
                                301 => 'Moved Permanently',
199
                                302 => 'Found',
200
                                303 => 'See Other',
201
                                304 => 'Not Modified',
202
                                305 => 'Use Proxy',
203
                                307 => 'Temporary Redirect',
204
                                400 => 'Bad Request',
205
                                401 => 'Unauthorized',
206
                                402 => 'Payment Required',
207
                                403 => 'Forbidden',
208
                                404 => 'Not Found',
209
                                405 => 'Method Not Allowed',
210
                                406 => 'Not Acceptable',
211
                                407 => 'Proxy Authentication Required',
212
                                408 => 'Request Timeout',
213
                                409 => 'Conflict',
214
                                410 => 'Gone',
215
                                411 => 'Length Required',
216
                                412 => 'Precondition Failed',
217
                                413 => 'Request Entity Too Large',
218
                                414 => 'Request-URI Too Long',
219
                                415 => 'Unsupported Media Type',
220
                                416 => 'Requested Range Not Satisfiable',
221
                                417 => 'Expectation Failed',
222
                                418 => 'I\'m a teapot',
223
                                500 => 'Internal Server Error',
224
                                501 => 'Not Implemented',
225
                                502 => 'Bad Gateway',
226
                                503 => 'Service Unavailable',
227
                                504 => 'Gateway Timeout',
228
                                505 => 'HTTP Version Not Supported',
229
                            );
230
            if (isset($http_status[$code])) {
231
                $text = $http_status[$code];
232
            } else {
233
                show_error('No HTTP status text found for your code please check it.');
234
            }
235
        }
236
		
237
        if (strpos(php_sapi_name(), 'cgi') === 0) {
238
            header('Status: ' . $code . ' ' . $text, TRUE);
239
        } else {
240
            $proto = 'HTTP/1.1';
241
            if (isset($_SERVER['SERVER_PROTOCOL'])) {
242
                $proto = $_SERVER['SERVER_PROTOCOL'];
243
            }
244
            header($proto . ' ' . $code . ' ' . $text, TRUE, $code);
245
        }
246
    }
247
248
    /**
249
     *  This function displays an error message to the user and ends the execution of the script.
250
     *  
251
     *  @param string $msg the message to display
252
     *  @param string $title the message title: "error", "info", "warning", etc.
253
     *  @param boolean $logging either to save error in log
254
     *  
255
     *  @codeCoverageIgnore
256
     */
257
    function show_error($msg, $title = 'error', $logging = true) {
258
        $title = strtoupper($title);
259
        $data = array();
260
        $data['error'] = $msg;
261
        $data['title'] = $title;
262
        if ($logging) {
263
            save_to_log('error', '[' . $title . '] ' . strip_tags($msg), 'GLOBAL::ERROR');
264
        }
265
        $response = & class_loader('Response', 'classes');
266
        //remove other content set to prevent duplicate view
267
        $response->setFinalPageContent(null);
268
        $response->render('errors', $data);
269
        $response->sendError();
270
        die();
271
    }
272
273
       /**
274
     *  Function defined for handling PHP exception error message, 
275
     *  it displays an error message using the function "show_error"
276
     *  
277
     *  @param object $ex instance of the "Exception" class or a derived class
278
     *  @codeCoverageIgnore
279
     *  
280
     *  @return boolean
281
     */
282
    function fw_exception_handler($ex) {
283
        if (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors'))) {
284
            show_error('An exception is occured in file ' . $ex->getFile() . ' at line ' . $ex->getLine() . ' raison : ' . $ex->getMessage(), 'PHP Exception #' . $ex->getCode());
285
        } else {
286
            save_to_log('error', 'An exception is occured in file ' . $ex->getFile() . ' at line ' . $ex->getLine() . ' raison : ' . $ex->getMessage(), 'PHP Exception');
287
        }
288
        return true;
289
    }
290
    
291
    /**
292
     *  Function defined for PHP error message handling
293
     *              
294
     *  @param int $errno the type of error for example: E_USER_ERROR, E_USER_WARNING, etc.
295
     *  @param string $errstr the error message
296
     *  @param string $errfile the file where the error occurred
297
     *  @param int $errline the line number where the error occurred
298
     *  @codeCoverageIgnore
299
     *  
300
     *  @return boolean 
301
     */
302
    function fw_error_handler($errno, $errstr, $errfile, $errline) {
303
        $isError = (((E_ERROR | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR) & $errno) === $errno);
304
        if ($isError) {
305
            set_http_status_header(500);
306
        }
307
        if (!(error_reporting() & $errno)) {
308
            save_to_log('error', 'An error is occurred in the file ' . $errfile . ' at line ' . $errline . ' raison : ' . $errstr, 'PHP ERROR');
309
            return;
310
        }
311
        if (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors'))) {
312
            $errorType = 'error';
313
            switch ($errno) {
314
                case E_USER_WARNING:
315
                    $errorType = 'warning';
316
                    break;
317
                case E_USER_NOTICE:
318
                    $errorType = 'notice';
319
                    break;
320
            }
321
            show_error('An error is occurred in the file <b>' . $errfile . '</b> at line <b>' . $errline . '</b> raison : ' . $errstr, 'PHP ' . $errorType);
322
        }
323
        if ($isError) {
324
            die();
325
        }
326
        return true;
327
    }
328
329
    /**
330
     * This function is used to run in shutdown situation of the script
331
     * @codeCoverageIgnore
332
     */
333
    function fw_shudown_handler() {
334
        $lastError = error_get_last();
335
        if (isset($lastError) &&
336
            ($lastError['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING))) {
337
            php_error_handler($lastError['type'], $lastError['message'], $lastError['file'], $lastError['line']);
0 ignored issues
show
Bug introduced by
The function php_error_handler was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

337
            /** @scrutinizer ignore-call */ 
338
            php_error_handler($lastError['type'], $lastError['message'], $lastError['file'], $lastError['line']);
Loading history...
338
        }
339
    }
340
341
    /**
342
     *  Check whether the protocol used is "https" or not
343
     *  That is, the web server is configured to use a secure connection.
344
     *  @codeCoverageIgnore
345
     *  
346
     *  @return boolean true if the web server uses the https protocol, false if not.
347
     */
348
    function is_https() {
349
        /*
350
		* some servers pass the "HTTPS" parameter in the server variable,
351
		* if is the case, check if the value is "on", "true", "1".
352
		*/
353
        if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') {
354
            return true;
355
        }
356
        if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
357
            return true;
358
        }
359
        if (isset($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off') {
360
            return true;
361
        }
362
        return false;
363
    }
364
	
365
    /**
366
     *  This function is used to check the URL format of the given string argument. 
367
     *  The address is valid if the protocol is http, https, ftp, etc.
368
     *
369
     *  @param string $url the URL address to check
370
     *  @test
371
     *  
372
     *  @return boolean true if is a valid URL address or false.
373
     */
374
    function is_url($url) {
375
        return preg_match('/^(http|https|ftp):\/\/(.*)/', $url) == 1;
376
    }
377
	
378
    /**
379
     *  Function defined to load controller
380
     *  
381
     *  @param string $controllerClass the controller class name to be loaded
382
     *  @codeCoverageIgnore
383
     */
384
    function autoload_controller($controllerClass) {
385
        if (file_exists($path = APPS_CONTROLLER_PATH . $controllerClass . '.php')) {
386
            require_once $path;
387
        }
388
    }
389
390
    /**
391
     *  Convert array attributes to string
392
     *
393
     *  This function converts an associative array into HTML attributes.
394
     *  For example :
395
     *  $a = array('name' => 'Foo', 'type' => 'text'); => produces the following string:
396
     *  name = "Foo" type = "text"
397
     *
398
     *  @param array $attributes associative array to convert to a string attribute.
399
     *   
400
     *  @return string string of the HTML attribute.
401
     */
402
    function attributes_to_string(array $attributes) {
403
        $str = ' ';
404
        //we check that the array passed as an argument is not empty.
405
        if (!empty($attributes)) {
406
            foreach ($attributes as $key => $value) {
407
                $key = trim(htmlspecialchars($key));
408
                $value = trim(htmlspecialchars($value));
409
                /*
410
				* To predict the case where the string to convert contains the character "
411
				* we check if this is the case we add a slash to solve this problem.
412
				* For example:
413
				* 	$attr = array('placeholder' => 'I am a "puple"')
414
				* 	$str = attributes_to_string($attr); => placeholder = "I am a \"puple\""
415
				 */
416
                if ($value && strpos('"', $value) !== false) {
417
                    $value = addslashes($value);
418
                }
419
                $str .= $key . ' = "' . $value . '" ';
420
            }
421
        }
422
        //remove the space after using rtrim()
423
        return rtrim($str);
424
    }
425
426
427
    /**
428
     * Function to stringfy PHP variable, useful in debug situation
429
     *
430
     * @param mixed $var the variable to stringfy
431
     * @codeCoverageIgnore
432
     *
433
     * @return string the stringfy value
434
     */
435
    function stringfy_vars($var) {
436
        return print_r($var, true);
437
    }
438
439
    /**
440
     * Clean the user input
441
     * @param  mixed $str the value to clean
442
     * @test
443
     * 
444
     * @return mixed   the sanitize value
445
     */
446
    function clean_input($str) {
447
        if (is_array($str)) {
448
            $str = array_map('clean_input', $str);
449
        } else if (is_object($str)) {
450
            $obj = $str;
451
            foreach ($str as $var => $value) {
452
                $obj->$var = clean_input($value);
453
            }
454
            $str = $obj;
455
        } else {
456
            $str = htmlspecialchars(strip_tags($str), ENT_QUOTES, 'UTF-8');
457
        }
458
        return $str;
459
    }
460
	
461
    /**
462
     * This function is used to hidden some part of the given string. Helpful if you need hide some confidential 
463
     * Information like credit card number, password, etc.
464
     *
465
     * @param  string $str the string you want to hide some part
466
     * @param  int $startCount the length of non hidden for the beginning char
467
     * @param  int $endCount the length of non hidden for the ending char
468
     * @param  string $hiddenChar the char used to hide the given string
469
     * @test
470
     * 
471
     * @return string the string with the hidden part.
472
     */
473
    function string_hidden($str, $startCount = 0, $endCount = 0, $hiddenChar = '*') {
474
        //get the string length
475
        $len = strlen($str);
476
        //if str is empty
477
        if ($len <= 0) {
478
            return str_repeat($hiddenChar, 6);
479
        }
480
        //if the length is less than startCount and endCount
481
        //or the startCount and endCount length is 0
482
        //or startCount is negative or endCount is negative
483
        //return the full string hidden
484
		
485
        if ((($startCount + $endCount) > $len) || ($startCount == 0 && $endCount == 0) || ($startCount < 0 || $endCount < 0)) {
486
            return str_repeat($hiddenChar, $len);
487
        }
488
        //the start non hidden string
489
        $startNonHiddenStr = substr($str, 0, $startCount);
490
        //the end non hidden string
491
        $endNonHiddenStr = null;
492
        if ($endCount > 0) {
493
            $endNonHiddenStr = substr($str, - $endCount);
494
        }
495
        //the hidden string
496
        $hiddenStr = str_repeat($hiddenChar, $len - ($startCount + $endCount));
497
		
498
        return $startNonHiddenStr . $hiddenStr . $endNonHiddenStr;
499
    }
500
	
501
    /**
502
     * This function is used to set the initial session config regarding the configuration
503
     * @codeCoverageIgnore
504
     */
505
    function set_session_config() {
506
        //$_SESSION is not available on cli mode 
507
        if (!IS_CLI) {
508
            $logger = & class_loader('Log', 'classes');
509
            $logger->setLogger('PHPSession');
510
            //set session params
511
            $sessionHandler = get_config('session_handler', 'files'); //the default is to store in the files
512
            $sessionName = get_config('session_name');
513
            if ($sessionName) {
514
                session_name($sessionName);
515
            }
516
            $logger->info('Session handler: ' . $sessionHandler);
517
            $logger->info('Session name: ' . $sessionName);
518
519
            if ($sessionHandler == 'files') {
520
                $sessionSavePath = get_config('session_save_path');
521
                if ($sessionSavePath) {
522
                    if (!is_dir($sessionSavePath)) {
523
                        mkdir($sessionSavePath, 1773);
524
                    }
525
                    session_save_path($sessionSavePath);
526
                    $logger->info('Session save path: ' . $sessionSavePath);
527
                }
528
            } else if ($sessionHandler == 'database') {
529
                //load database session handle library
530
                //Database Session handler Model
531
                require_once CORE_CLASSES_MODEL_PATH . 'DBSessionHandlerModel.php';
532
533
                $DBS = & class_loader('DBSessionHandler', 'classes');
534
                session_set_save_handler($DBS, true);
535
                $logger->info('session save path: ' . get_config('session_save_path'));
536
            } else {
537
                show_error('Invalid session handler configuration');
538
            }
539
            $lifetime = get_config('session_cookie_lifetime', 0);
540
            $path = get_config('session_cookie_path', '/');
541
            $domain = get_config('session_cookie_domain', '');
542
            $secure = get_config('session_cookie_secure', false);
543
            if (is_https()) {
544
                $secure = true;
545
            }
546
            session_set_cookie_params(
547
                $lifetime,
548
                $path,
549
                $domain,
550
                $secure,
551
                $httponly = true /*for security for access to cookie via javascript or XSS attack*/
552
            );
553
            //to prevent attack of Session Fixation 
554
            //thank to https://www.phparch.com/2018/01/php-sessions-in-depth/
555
            ini_set('session.use_strict_mode ', 1);
556
            ini_set('session.use_only_cookies', 1);
557
            ini_set('session.use_trans_sid ', 0);
558
			
559
            $logger->info('Session lifetime: ' . $lifetime);
560
            $logger->info('Session cookie path: ' . $path);
561
            $logger->info('Session domain: ' . $domain);
562
            $logger->info('Session is secure: ' . ($secure ? 'TRUE' : 'FALSE'));
563
			
564
            if ((function_exists('session_status') && session_status() !== PHP_SESSION_ACTIVE) || !session_id()) {
565
                $logger->info('Session not yet start, start it now');
566
                session_start();
567
            }
568
        }
569
    }
570
	
571
    /**
572
     * This function is very useful, it allows to recover the instance of the global controller.
573
     * Note this function always returns the address of the super instance.
574
     * For example :
575
     * $obj = & get_instance();
576
     * 
577
     * @codeCoverageIgnore
578
     *  
579
     * @return object the instance of the "Controller" class
580
     */
581
    function & get_instance(){
582
        return Controller::get_instance();
583
    }
584