1: <?php
2: /**
3: * This file is part of the PHPLucidFrame library.
4: *
5: * @package PHPLucidFrame\Core
6: * @since PHPLucidFrame v 1.0.0
7: * @copyright Copyright (c), PHPLucidFrame.
8: * @link http://phplucidframe.com
9: * @license http://www.opensource.org/licenses/mit-license.php MIT License
10: *
11: * This source file is subject to the MIT license that is bundled
12: * with this source code in the file LICENSE
13: */
14:
15: use LucidFrame\Core\SchemaManager;
16:
17: /**
18: * @ignore Flag for image resize to the fitted dimension to the given dimension
19: */
20: define('FILE_RESIZE_BOTH', 'both');
21: /**
22: * @ignore Flag for image resize to the given height, but width is aspect ratio of the height
23: */
24: define('FILE_RESIZE_HEIGHT', 'height');
25: /**
26: * @ignore Flag for image resize to the given width, but height is aspect ratio of the width
27: */
28: define('FILE_RESIZE_WIDTH', 'width');
29: /**
30: * @ignore File upload error flag for the failure of `move_uploaded_file()`
31: */
32: define('FILE_UPLOAD_ERR_MOVE', 100);
33: /**
34: * @ignore File upload error flag for the failure of image creation of GD functions
35: */
36: define('FILE_UPLOAD_ERR_IMAGE_CREATE', 101);
37: /**
38: * Query fetch types
39: */
40: define('LC_FETCH_ASSOC', 1);
41: define('LC_FETCH_ARRAY', 2);
42: define('LC_FETCH_OBJECT', 3);
43: /**
44: * Console command option types
45: */
46: define('LC_CONSOLE_OPTION_REQUIRED', 4);
47: define('LC_CONSOLE_OPTION_OPTIONAL', 5);
48: define('LC_CONSOLE_OPTION_NOVALUE', 6);
49:
50: /**
51: * @internal
52: * @ignore
53: * HTTP status code
54: */
55: $lc_httpStatusCode = 200;
56: /**
57: * @internal
58: * @ignore
59: * Site-wide warnings to be shown
60: */
61: $lc_sitewideWarnings = array();
62: /**
63: * @internal
64: * @ignore
65: * Auto load/unload configuration
66: */
67: $lc_autoload = array();
68: /**
69: * @internal
70: * @ignore
71: * Namespace which will later be available as a constant LC_NAMESPACE
72: */
73: $lc_namespace = '';
74: /**
75: * @internal
76: * @ignore
77: * The clean route without query string or without file name
78: */
79: $lc_cleanRoute = '';
80: /**
81: * @internal
82: * @ignore
83: * The global javascript variables that will be rentered in the <head> section
84: */
85: $lc_jsVars = array();
86: /**
87: * @internal
88: * @ignore
89: * The canonical URL for the current page
90: */
91: $lc_canonical = '';
92: /**
93: * @internal
94: * @ignore
95: * The array of configurations from parameter.env.inc
96: */
97: $lc_envParameters = null;
98: /**
99: * @internal
100: * @ignore
101: * Meta information for the current page
102: */
103: $_meta = array();
104: /**
105: * @internal
106: * @ignore
107: * @type array It contains the built and executed queries through out the script execution
108: */
109: global $db_builtQueries;
110: $db_builtQueries = array();
111: $db_printQuery = false;
112:
113: /***************************/
114: /* Internal functions here */
115: /***************************/
116:
117: /**
118: * @internal
119: * @ignore
120: * Prerequisite check
121: */
122: function __prerequisite()
123: {
124: if (version_compare(phpversion(), '5.3.0', '<')) {
125: die('PHPLucidFrame requires at least PHP 5.3.0. Your PHP installation is ' . phpversion() . '.');
126: }
127:
128: /**
129: * Check config.php
130: */
131: if (!file_exists(INC . 'config.php')) {
132: copy(INC . 'config.default.php', INC . 'config.php');
133: }
134:
135: if (PHP_SAPI !== 'cli') {
136: register_shutdown_function('__kernelShutdownHandler');
137: }
138: }
139:
140: /**
141: * @internal
142: * @ignore
143: * Dot notation access to multi-dimensional array
144: * Get the values by providing dot notation string key
145: * Set the values by providing dot notation string key
146: *
147: * @param string $key The string separated by dot (period)
148: * @param string $scope What scope in which the values will be stored - global or session
149: * @param mixed $value The optional value to set or updated
150: * @param boolean $serialize The value is to be serialized or not
151: *
152: * @return mixed The value assigned
153: */
154: function __dotNotationToArray($key, $scope = 'global', $value = '', $serialize = false)
155: {
156: if (empty($key)) {
157: return null;
158: }
159:
160: if (!in_array($scope, array('global', 'session')) && !is_array($scope)) {
161: return null;
162: }
163:
164: if (is_array($scope)) {
165: $input = &$scope;
166: }
167:
168: $type = count(func_get_args()) > 2 ? 'setter' : 'getter';
169: $keys = explode(".", $key);
170: # extract the first key
171: $firstKey = array_shift($keys);
172: # extract the last key
173: $lastKey = end($keys);
174: # No. of keys exclusive of the first key
175: $count = count($keys); # more than 0 if there is at least one dot
176: $justOneLevelKey = ($count === 0);
177:
178: if ($type == 'getter' && $justOneLevelKey) {
179: # just one-level key
180: if ($scope == 'session' && __sessionLoadable()) {
181: $firstKey = S_PREFIX . $firstKey;
182: return (array_key_exists($firstKey, $_SESSION)) ? $_SESSION[$firstKey] : null;
183: } elseif ($scope == 'global') {
184: return (array_key_exists($firstKey, $GLOBALS)) ? $GLOBALS[$firstKey] : null;
185: } elseif (is_array($scope) && isset($input)) {
186: return (array_key_exists($firstKey, $input)) ? $input[$firstKey] : null;
187: }
188: }
189:
190: $current = null;
191: if ($scope == 'session' && __sessionLoadable()) {
192: $firstKey = S_PREFIX . $firstKey;
193: if (!array_key_exists($firstKey, $_SESSION)) {
194: $_SESSION[$firstKey] = null;
195: }
196: $current = &$_SESSION[$firstKey];
197: } elseif ($scope == 'global') {
198: if (!array_key_exists($firstKey, $GLOBALS)) {
199: $GLOBALS[$firstKey] = null;
200: }
201: $current = &$GLOBALS[$firstKey];
202: } elseif (is_array($scope) && isset($input)) {
203: if (!array_key_exists($firstKey, $input)) {
204: $input[$firstKey] = null;
205: }
206: $current = &$input[$firstKey];
207: }
208:
209: $theLastHasValue = false;
210: if (($type == 'setter' && $count) || ($type == 'getter' && $count > 1)) {
211: # this will be skipped if no dot notation
212: foreach ($keys as $k) {
213: if ($k == $lastKey && isset($current[$lastKey])) {
214: if ($type === 'getter') {
215: return $current[$lastKey];
216: }
217:
218: $theLastHasValue = true;
219: if ($scope != 'session') {
220: # if the last-key has the value of not-array, create array and push the later values.
221: $current[$lastKey] = is_array($current[$k]) ? $current[$k] : array($current[$k]);
222: }
223: break;
224: }
225: if ($count && !isset($current[$k]) && !is_array($current)) {
226: $current = array($k => null);
227: }
228: $current = &$current[$k];
229: }
230: }
231: # Set the values if it is setter
232: if ($type == 'setter') {
233: if (is_array($current) && $theLastHasValue) {
234: # when $theLastHasValue, dot notation is given and it is array
235: $current[$lastKey] = ($serialize) ? serialize($value) : $value;
236: } else {
237: $current = ($serialize) ? serialize($value) : $value;
238: }
239: return $current;
240: } elseif ($type == 'getter') {
241: # Get the values if it is getter
242: return $count ? (isset($current[$lastKey]) ? $current[$lastKey] : null) : $current;
243: }
244: return null;
245: }
246:
247: /**
248: * @internal
249: * @ignore
250: * Load running environment settings
251: * Initialize the site language(s), error reporting
252: * Define two constants - REQUEST_URI and LC_NAMESPACE
253: *
254: * @return void
255: */
256: function __envLoader()
257: {
258: global $lc_languages;
259: global $lc_baseURL;
260: global $lc_sites;
261: global $lc_env;
262: global $lc_debugLevel;
263: global $lc_minifyHTML;
264: global $lc_timeZone;
265: global $lc_memoryLimit;
266: global $lc_maxExecTime;
267:
268: /**
269: * Don't escape quotes when reading files from the database, disk, etc.
270: */
271: ini_set('magic_quotes_runtime', '0');
272: /**
273: * Set the maximum amount of memory in bytes that a script is allowed to allocate.
274: * This helps prevent poorly written scripts for eating up all available memory on a server
275: */
276: ini_set('memory_limit', $lc_memoryLimit);
277: /**
278: * Set the maximum time in seconds a script is allowed to run before it is terminated by the parser.
279: * This helps prevent poorly written scripts from tying up the server. The default setting is 30.
280: */
281: ini_set('max_execution_time', $lc_maxExecTime);
282:
283: /**
284: * Default Time Zone
285: */
286: date_default_timezone_set($lc_timeZone);
287:
288: $lc_env = strtolower($lc_env);
289: if (!in_array($lc_env, __envList())) {
290: $lc_env = ENV_PROD;
291: }
292:
293: $lc_minifyHTML = $lc_env == ENV_PROD;
294: switch ($lc_debugLevel) {
295: case 0:
296: error_reporting(0);
297: ini_set('display_errors', 0);
298: ini_set('display_startup_errors', 0);
299: break;
300:
301: case 1:
302: error_reporting(E_ERROR | E_PARSE);
303: ini_set('display_errors', 1);
304: ini_set('display_startup_errors', 0);
305: break;
306:
307: case 2:
308: error_reporting(E_ERROR | E_PARSE | E_NOTICE | E_WARNING);
309: ini_set('display_errors', 1);
310: ini_set('display_startup_errors', 1);
311: break;
312:
313: case 3:
314: error_reporting(E_ALL);
315: ini_set('display_errors', 1);
316: ini_set('display_startup_errors', 1);
317: break;
318:
319: default:
320: error_reporting($lc_debugLevel); // customer debug level
321: ini_set('display_errors', 1);
322: ini_set('display_startup_errors', 1);
323: }
324:
325: if (empty($lc_languages) || !is_array($lc_languages)) {
326: $lc_languages = array('en' => 'English');
327: }
328:
329: $REQUEST_URI = $_SERVER['REQUEST_URI'];
330:
331: $requestURI = substr($REQUEST_URI, strpos($REQUEST_URI, '/'.$lc_baseURL) + strlen($lc_baseURL) + 1);
332: $requestURI = ltrim($requestURI, '/');
333: $request = explode('/', $requestURI);
334: $lc_namespace = $request[0];
335:
336: if (PHP_SAPI == 'cli' && $_SERVER['SCRIPT_NAME'] == 'lucidframe' && $_SERVER['argc'] == 2) {
337: $cmd = explode(':', $_SERVER['argv'][1]);
338: $lc_namespace = count($cmd) > 1 ? $cmd[0] : '';
339: }
340:
341: # Clean lang code in URL
342: if (array_key_exists($lc_namespace, $lc_languages)) {
343: array_shift($request);
344: $requestURI = ltrim(ltrim($requestURI, $lc_namespace), '/'); # clean the language code from URI
345: $lc_namespace = count($request) ? $request[0] : '';
346: }
347:
348: if (!(isset($lc_sites) && is_array($lc_sites) && array_key_exists($lc_namespace, $lc_sites))) {
349: $lc_namespace = '';
350: }
351:
352: # REQUEST_URI excluding the base URL
353: define('REQUEST_URI', trim($requestURI, '/'));
354: # Namespace according to the site directories
355: define('LC_NAMESPACE', $lc_namespace);
356:
357: unset($requestURI);
358: unset($request);
359: }
360:
361: /**
362: * @internal
363: * @ignore
364: * Read .secret and return the hash string which is the value for $lc_securitySecret
365: * @param string $file The optional file path
366: * @return string
367: */
368: function __secret($file = null)
369: {
370: if ($file !== null && is_file($file) && file_exists($file)) {
371: return trim(file_get_contents($file));
372: }
373:
374: $path = [
375: ROOT . '.secret',
376: INC . '.secret', # TODO: for backward compatibility, to be removed
377: ];
378:
379: foreach ($path as $file) {
380: if (is_file($file) && file_exists($file)) {
381: return trim(file_get_contents($file));
382: }
383: }
384:
385: return '';
386: }
387:
388: /**
389: * @internal
390: * @ignore
391: * Read and get the environment setting from .lcenv
392: * @return string
393: */
394: function __env()
395: {
396: $defaultEnv = ENV_DEV;
397:
398: $oldFile = ROOT . '.env';
399: if (is_file($oldFile) && file_exists($oldFile)) {
400: $defaultEnv = trim(file_get_contents($oldFile));
401: if (in_array($defaultEnv, __envList())) {
402: unlink($oldFile);
403: }
404: }
405:
406: $file = ROOT . FILE_ENV;
407: if (!(is_file($file) && file_exists($file))) {
408: file_put_contents($file, $defaultEnv);
409: }
410:
411: $env = trim(file_get_contents($file));
412: if (!in_array($env, __envList())) {
413: $env = ENV_PROD;
414: }
415:
416: return $env;
417: }
418:
419: /**
420: * @internal
421: * @ignore
422: * Return list of env name array
423: * @return array
424: */
425: function __envList()
426: {
427: return array(ENV_PROD, ENV_STAGING, ENV_DEV, ENV_TEST, 'dev', 'prod');
428: }
429:
430: /**
431: * @internal
432: * @ignore
433: *
434: * Custom error handler
435: *
436: * @param integer $code Error code
437: * @param string $message Error message
438: * @param string $file File name
439: * @param integer $line Error line number
440: * @return boolean
441: */
442: function __kernelErrorHandler($code, $message, $file, $line)
443: {
444: if (!(error_reporting() & $code)) {
445: // This error code is not included in error_reporting, so let it fall
446: // through to the standard PHP error handler
447: return false;
448: }
449:
450: $type = __kernelErrorTypes($code);
451: $trace = array_reverse(debug_backtrace());
452:
453: $status = _g('httpStatusCode');
454: if (empty($status) || $status == 200) {
455: $status = 500;
456: _g('httpStatusCode', $status);
457: }
458:
459: _header($status);
460:
461: include( _i('inc/tpl/exception.php') );
462: exit;
463: }
464:
465: /**
466: * @internal
467: * @ignore
468: *
469: * Custom shutdown handler
470: */
471: function __kernelShutdownHandler()
472: {
473: $error = error_get_last();
474:
475: if (is_array($error)) {
476: if (__env() == ENV_PROD || error_reporting() == 0) {
477: _log($error);
478: }
479:
480: __kernelErrorHandler($error['type'], $error['message'], $error['file'], $error['line']);
481: }
482: }
483:
484: /**
485: * @internal
486: * @ignore
487: *
488: * Get friendly error type by code
489: * @param integer $code Error code
490: * @return string The friendly error type
491: */
492: function __kernelErrorTypes($code)
493: {
494: switch($code) {
495: case E_ERROR: # 1
496: return 'E_ERROR: Fatal error';
497:
498: case E_WARNING: # 2
499: return 'E_WARNING: Warning';
500:
501: case E_PARSE: # 4
502: return 'E_PARSE: Parse error';
503:
504: case E_NOTICE: # 8
505: return 'E_NOTICE: Notice';
506:
507: case E_CORE_ERROR: # 16
508: return 'E_CORE_ERROR: Fatal error';
509:
510: case E_CORE_WARNING: # 32
511: return 'E_CORE_WARNING: Warning';
512:
513: case E_COMPILE_ERROR: # 64
514: return 'E_COMPILE_ERROR: Fatal error';
515:
516: case E_COMPILE_WARNING: # 128
517: return 'E_COMPILE_WARNING: Warning';
518:
519: case E_USER_ERROR: # 256
520: return 'E_USER_ERROR: User-generated error';
521:
522: case E_USER_WARNING: # 512
523: return 'E_USER_WARNING: User-generated warning';
524:
525: case E_USER_NOTICE: # 1024
526: return 'E_USER_NOTICE: User-generated notice';
527:
528: case E_STRICT: # 2048
529: return 'E_STRICT: Information';
530:
531: case E_RECOVERABLE_ERROR: # 4096
532: return 'E_RECOVERABLE_ERROR: Catchable fatal error';
533:
534: case E_DEPRECATED: # 8192
535: return 'E_DEPRECATED: Deprecated warning';
536:
537: case E_USER_DEPRECATED: # 16384
538: return 'E_USER_DEPRECATED: User-generated deprecated warning';
539: }
540:
541: return 'E_ERROR, Error';
542: }
543:
544: /**
545: * Autoload helper
546: * @param string|array $modules The module file name
547: */
548: function __autoloadHelper($modules)
549: {
550: $modules = is_array($modules) ? $modules : array($modules);
551: $helperDirs = _baseDirs('helpers');
552:
553: foreach ($modules as $helper) {
554: foreach ($helperDirs as $dir) {
555: $moduleFile = $dir . $helper . '_helper.php';
556: if (is_file($moduleFile) && file_exists($moduleFile)) {
557: include($moduleFile);
558: }
559: }
560: }
561: }
562:
563: /**
564: * @internal
565: * @ignore
566: *
567: * Check if db is loadable (skip db initialization upon some CLI commands execution)
568: * @return bool
569: */
570: function __dbLoadable()
571: {
572: global $argv;
573:
574: return !(PHP_SAPI == 'cli'
575: && stripos($argv[0], 'lucidframe') !== false
576: && isset($argv[1]) && in_array($argv[1], ['list', 'env', 'secret:generate']));
577: }
578:
579: /**
580: * @internal
581: * @ignore
582: *
583: * Check if session is loadable (skip session initialization upon some CLI commands execution)
584: * @return bool
585: */
586: function __sessionLoadable()
587: {
588: return PHP_SAPI != 'cli';
589: }
590:
591: /*************************/
592: /* Public functions here */
593: /*************************/
594:
595: /**
596: * Get schema definition file
597: * @param string $dbNamespace The namespace for the database
598: * @param boolean $cache TRUE to look for the file in /db/build/; FALSE in /db/
599: * `TRUE` to look for the file in such priority
600: * 1. /db/build/schema.{namespace}.lock
601: * 2. /db/build/schema.lock (only for default)
602: * 3. /db/schema.{namespace}.php
603: * 4. /db/schema.php (only for default)
604: *
605: * `FALSE` to look in this priority
606: * 1. /db/schema.{namespace}.php
607: * 2. /db/schema.php (only for default)
608: *
609: * @return mixed
610: * array The schema definition
611: * null Incorrect schema definition
612: * boolean False when the file doesn't exist
613: */
614: function _schema($dbNamespace = 'default', $cache = false)
615: {
616: $files = array();
617: if ($cache) {
618: $files[] = SchemaManager::getSchemaLockFileName($dbNamespace);
619: $files[] = SchemaManager::getSchemaLockFileName();
620: }
621:
622: $files[] = DB."schema.{$dbNamespace}.php";
623: $files[] = DB."schema.php";
624:
625: foreach ($files as $f) {
626: if (is_file($f) && file_exists($f)) {
627: $file = $f;
628: if (pathinfo($file, PATHINFO_EXTENSION) == 'lock') {
629: return unserialize(file_get_contents($file));
630: } else {
631: $schema = include($file);
632: return is_array($schema) ? $schema : null;
633: }
634: }
635: }
636:
637: return false;
638: }
639:
640: /**
641: * File include helper
642: * Find files under the default directories inc/, js/, css/ according to the defined site directories $lc_sites
643: *
644: * @param $file string File name with directory path
645: * @param $recursive boolean True to find the file name until the site root
646: *
647: * @return string File name with absolute path if it is found, otherwise return an empty string
648: */
649: function _i($file, $recursive = true)
650: {
651: global $lc_baseURL;
652: global $lc_sites;
653: global $lc_languages;
654:
655: $ext = strtolower(substr($file, strrpos($file, '.')+1)); # get the file extension
656: if (in_array($ext, array('js', 'css'))) {
657: $appRoot = WEB_APP_ROOT;
658: $root = WEB_ROOT;
659: } else {
660: $appRoot = APP_ROOT;
661: $root = ROOT;
662: }
663:
664: if (!is_array($lc_languages)) {
665: $lc_languages = array('en' => 'English');
666: }
667:
668: $REQUEST_URI = $_SERVER['REQUEST_URI'];
669:
670: $requestURI = trim(ltrim($REQUEST_URI, '/'.$lc_baseURL)); # /base-dir/path/to/sub/dir to path/to/sub/dir
671: $request = explode('/', $requestURI);
672:
673: $needle = $request[0];
674: # Clean lang code in URL
675: if (array_key_exists($needle, $lc_languages)) {
676: array_shift($request);
677: }
678:
679: $folders = array();
680: if (LC_NAMESPACE == '') {
681: # Find in APP_ROOT -> ROOT
682: $folders = array(
683: APP_ROOT => $appRoot,
684: ROOT => $root
685: );
686: }
687:
688: if (isset($lc_sites) && is_array($lc_sites) && count($lc_sites)) {
689: if (array_key_exists(LC_NAMESPACE, $lc_sites)) {
690: # Find in SUB-DIR -> APP_ROOT -> ROOT
691: $folders = array(
692: APP_ROOT.$lc_sites[LC_NAMESPACE]._DS_ => $appRoot . $lc_sites[LC_NAMESPACE] . _DS_,
693: APP_ROOT => $appRoot,
694: ROOT => $root
695: );
696: }
697: }
698:
699: # $key is for file_exists()
700: # $value is for include() or <script> or <link>
701: foreach ($folders as $key => $value) {
702: if ($key === ROOT && substr($file, 0, 7) === 'helpers') {
703: $fileWithPath = LIB . $file;
704: $libHelper = true;
705: } else {
706: $fileWithPath = $key . $file;
707: $libHelper = false;
708: }
709:
710: if (is_file($fileWithPath) && file_exists($fileWithPath)) {
711: if ($libHelper === false) {
712: $fileWithPath = $value . $file;
713: }
714:
715: return $fileWithPath;
716: }
717:
718: if ($recursive == false) {
719: break;
720: }
721: }
722:
723: if (strstr($_SERVER['PHP_SELF'], APP_DIR)) {
724: if ($recursive == true) {
725: if ($root === ROOT && substr($file, 0, 7) === 'helpers') {
726: $file = LIB . $file;
727: } else {
728: $file = $root . $file;
729: }
730: } else {
731: $file = $root . $file;
732: }
733:
734: if (is_file($file) && file_exists($file)) {
735: return $file;
736: }
737: }
738:
739: return '';
740: }
741:
742: /**
743: * Get the host name or server name
744: * @return mixed|string
745: */
746: function _host()
747: {
748: if (isset($_SERVER['HTTP_HOST'])) {
749: return $_SERVER['HTTP_HOST'];
750: }
751:
752: if (isset($_SERVER['SERVER_NAME'])) {
753: return $_SERVER['SERVER_NAME'];
754: }
755:
756: return _env('host');
757: }
758:
759: /**
760: * Convenience method to get/set a config variable without global declaration within the calling function
761: *
762: * @param string $key The config variable name without prefix
763: * @param mixed $value The value to set to the config variable; if it is omitted, it is Getter method.
764: * @return mixed The value of the config variable
765: */
766: function _cfg($key, $value = '')
767: {
768: if (strrpos($key, 'lc_') === 0) {
769: $key = substr($key, 3);
770: }
771:
772: $key = 'lc_' . $key;
773:
774: return count(func_get_args()) == 2 ? __dotNotationToArray($key, 'global', $value) : __dotNotationToArray($key, 'global');
775: }
776:
777: /**
778: * Convenience method to get the value of the array config variable by its key
779: *
780: * @param string $name The config array variable name without prefix
781: * @param string $key The key of the config array of which value to be retrieved
782: * @return mixed|string|null The value of a single column of the config array variable
783: */
784: function _cfgOption($name, $key)
785: {
786: $config = _cfg($name);
787:
788: return isset($config[$key]) ? $config[$key] : null;
789: }
790:
791: /**
792: * Get the parameter value by name defined in `/inc/parameter/(development|production|staging|test).php`
793: * @param string $name The parameter name defined as key in `/inc/parameter/(development|production|staging|test).php`.
794: * The file development, production, staging or test will be determined according to the value from `.lcenv`.
795: * If `$name` is `env` (by default), it returns the current environment setting from `.lcenv`.
796: * @return mixed The value defined `/inc/parameter/(development|production|staging|test).php`
797: */
798: function _p($name = 'env')
799: {
800: if ($name == 'env') {
801: return __env();
802: }
803:
804: global $argv;
805:
806: if (PHP_SAPI == 'cli' && isset($argv[0]) && stripos($argv[0], 'lucidframe') !== false) {
807: # keep the current environment when `php lucidframe` is run
808: $env = _cfg('env');
809: } elseif (PHP_SAPI == 'cli' || stripos($_SERVER['REQUEST_URI'], 'tests/') !== false) {
810: # force change to "test" environment when run `php tests/tests.php` from CLI
811: # or when run `/tests/tests.php` from browser
812: $env = 'test';
813: _cfg('env', $env);
814: } else {
815: # neither CLI nor test
816: $env = _cfg('env');
817: }
818:
819: if (!in_array($env, __envList())) {
820: die(sprintf('Wrong environment configuration. Use "%s" or "%s" or "%s" or "%s".', ENV_DEV, ENV_STAGING, ENV_PROD, ENV_TEST));
821: }
822:
823: $param = include(INC . 'parameter/' . $env . '.php');
824:
825: return __dotNotationToArray($name, $param);
826: }
827:
828: /**
829: * Convenience method to get/set a global variable
830: *
831: * @param string $key The global variable name
832: * @param mixed $value The value to set to the global variable; if it is not given, it is Getter method.
833: * @return mixed The value of the global variable
834: */
835: function _g($key, $value = '')
836: {
837: if (empty($key)) {
838: return null;
839: }
840:
841: if (count(func_get_args()) == 2) {
842: return __dotNotationToArray($key, 'global', $value);
843: } else {
844: return __dotNotationToArray($key);
845: }
846: }
847:
848: /**
849: * Get the parameter value by name defined in `/inc/parameter/env.inc`
850: * @param string $name The parameter name in dot annotation format such as `prod.db.default.database`
851: * @param mixed $default The default value if the parameter name doesn't exist
852: * @return mixed The value defined in `/inc/parameter/env.inc`
853: */
854: function _env($name, $default = '')
855: {
856: global $lc_envParameters;
857:
858: if ($lc_envParameters === null) {
859: $files = array(
860: INC . 'parameter/env.inc',
861: INC . 'parameter/parameter.env.inc', # TODO: for backward compatibility, to be removed
862: );
863:
864: foreach ($files as $file) {
865: if (is_file($file) && file_exists($file)) {
866: $lc_envParameters = include($file);
867: break;
868: }
869: }
870: }
871:
872: $value = __dotNotationToArray($name, $lc_envParameters);
873:
874: return $value ?: $default;
875: }
876:
877: /**
878: * Get base URL with protocol
879: * @return string
880: */
881: function _baseUrlWithProtocol()
882: {
883: $baseUrl = _cfg('baseURL');
884: $protocol = _cfg('ssl') ? 'https' : 'http';
885:
886: if (PHP_SAPI == 'cli') {
887: $base = trim(_p('siteDomain'), '/');
888: } else {
889: $base = strtolower($protocol) . '://';
890: $base .= $_SERVER['HTTP_HOST'];
891: }
892:
893: if ($baseUrl) {
894: $base .= '/' . $baseUrl;
895: }
896:
897: return $base;
898: }
899:
900: /**
901: * Get base directory list by priority
902: * @param string $subDir The subdirectory name
903: * @return string[]
904: */
905: function _baseDirs($subDir = '')
906: {
907: $folders = array();
908:
909: $namespace = LC_NAMESPACE;
910: if (!empty($_GET['lc_namespace'])) {
911: $namespace = $_GET['lc_namespace'];
912: }
913:
914: $sites = _cfg('sites');
915: if (count($sites) && array_key_exists($namespace, $sites)) {
916: $folders[] = rtrim(APP_ROOT . $sites[$namespace] . _DS_ . $subDir, _DS_) . _DS_;
917: }
918:
919: $folders[] = rtrim(APP_ROOT . $subDir, _DS_) . _DS_;
920: $folders[] = rtrim(LIB . $subDir, _DS_) . _DS_;
921:
922: return $folders;
923: }
924:
925: /**
926: * Write log to file
927: *
928: * @param array|string $msg The message to log
929: * @param string $file The destination file name
930: * @param int $type 0 ~ 4 See error_log() at https://www.php.net/manual/en/function.error-log
931: * @return bool
932: */
933: function _log($msg, $file = '', $type = 3)
934: {
935: if ($file) {
936: $file = $file . '-'. date('Ymd') . '.log';
937: } else {
938: $file = 'log-'. date('Ymd') . '.log';
939: }
940: $file = LOG . $file;
941: $file = substr($file, 0, strrpos($file, '.log'));
942: $file .= '-' . __env();
943: if (strtolower(php_sapi_name()) == 'cli') {
944: $file .= '.cli';
945: }
946: $file .= '.log';
947:
948: if (is_array($msg)) {
949: if (isset($msg['message'])) {
950: $msg = $msg['message'] . ' in ' . $msg['file'] . ' on line ' . $msg['line'];
951: } else {
952: $msg = print_r($msg, true);
953: }
954: }
955:
956: $msg = '[' . date('Y-m-d H:i:s') . '] : ' . trim($msg) . PHP_EOL;
957:
958: return error_log($msg, $type, $file);
959: }
960:
961:
962: __prerequisite();
963: