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: if ($lc_env == ENV_PROD) {
293: error_reporting(0);
294: ini_set('display_errors', 0);
295: ini_set('display_startup_errors', 0);
296: } else {
297: $lc_minifyHTML = false;
298: switch($lc_debugLevel) {
299: case 1:
300: error_reporting(E_ERROR | E_PARSE);
301: ini_set('display_errors', 1);
302: ini_set('display_startup_errors', 0);
303: break;
304: case 2:
305: error_reporting(E_ERROR | E_PARSE | E_NOTICE | E_WARNING);
306: ini_set('display_errors', 1);
307: ini_set('display_startup_errors', 1);
308: break;
309: case 3:
310: error_reporting(E_ALL);
311: ini_set('display_errors', 1);
312: ini_set('display_startup_errors', 1);
313: break;
314: default:
315: error_reporting($lc_debugLevel);
316: ini_set('display_errors', 1);
317: ini_set('display_startup_errors', 1);
318: }
319: }
320:
321: if (empty($lc_languages) || !is_array($lc_languages)) {
322: $lc_languages = array('en' => 'English');
323: }
324:
325: $REQUEST_URI = $_SERVER['REQUEST_URI'];
326:
327: $requestURI = substr($REQUEST_URI, strpos($REQUEST_URI, '/'.$lc_baseURL) + strlen($lc_baseURL) + 1);
328: $requestURI = ltrim($requestURI, '/');
329: $request = explode('/', $requestURI);
330: $lc_namespace = $request[0];
331:
332: # Clean lang code in URL
333: if (array_key_exists($lc_namespace, $lc_languages)) {
334: array_shift($request);
335: $requestURI = ltrim(ltrim($requestURI, $lc_namespace), '/'); # clean the language code from URI
336: $lc_namespace = count($request) ? $request[0] : '';
337: }
338:
339: if (!(isset($lc_sites) && is_array($lc_sites) && array_key_exists($lc_namespace, $lc_sites))) {
340: $lc_namespace = '';
341: }
342:
343: # REQUEST_URI excluding the base URL
344: define('REQUEST_URI', trim($requestURI, '/'));
345: # Namespace according to the site directories
346: define('LC_NAMESPACE', $lc_namespace);
347:
348: unset($requestURI);
349: unset($request);
350: }
351:
352: /**
353: * @internal
354: * @ignore
355: * Read .secret and return the hash string which is the value for $lc_securitySecret
356: * @param string $file The optional file path
357: * @return string
358: */
359: function __secret($file = null)
360: {
361: if ($file !== null && is_file($file) && file_exists($file)) {
362: return trim(file_get_contents($file));
363: }
364:
365: $file = INC . '.secret';
366: return (is_file($file) && file_exists($file)) ? trim(file_get_contents($file)) : '';
367: }
368:
369: /**
370: * @internal
371: * @ignore
372: * Read and get the environment setting from .lcenv
373: * @return string
374: */
375: function __env()
376: {
377: $defaultEnv = ENV_DEV;
378:
379: $oldFile = ROOT . '.env';
380: if (is_file($oldFile) && file_exists($oldFile)) {
381: $defaultEnv = trim(file_get_contents($oldFile));
382: if (in_array($defaultEnv, __envList())) {
383: unlink($oldFile);
384: }
385: }
386:
387: $file = ROOT . FILE_ENV;
388: if (!(is_file($file) && file_exists($file))) {
389: file_put_contents($file, $defaultEnv);
390: }
391:
392: $env = trim(file_get_contents($file));
393: if (!in_array($env, __envList())) {
394: $env = ENV_PROD;
395: }
396:
397: return $env;
398: }
399:
400: /**
401: * @internal
402: * @ignore
403: * Return list of env name array
404: * @return array
405: */
406: function __envList()
407: {
408: return array(ENV_PROD, ENV_STAGING, ENV_DEV, ENV_TEST, 'dev', 'prod');
409: }
410:
411: /**
412: * @internal
413: * @ignore
414: *
415: * Custom error handler
416: *
417: * @param integer $code Error code
418: * @param string $message Error message
419: * @param string $file File name
420: * @param integer $line Error line number
421: * @return boolean
422: */
423: function __kernelErrorHandler($code, $message, $file, $line)
424: {
425: if (!(error_reporting() & $code)) {
426: // This error code is not included in error_reporting, so let it fall
427: // through to the standard PHP error handler
428: return false;
429: }
430:
431: $type = __kernelErrorTypes($code);
432: $trace = array_reverse(debug_backtrace());
433:
434: $status = _g('httpStatusCode');
435: if (empty($status) || $status == 200) {
436: $status = 500;
437: _g('httpStatusCode', $status);
438: }
439:
440: _header($status);
441:
442: include( _i('inc/tpl/exception.php') );
443: exit;
444: }
445:
446: /**
447: * @internal
448: * @ignore
449: *
450: * Custom shutdown handler
451: */
452: function __kernelShutdownHandler()
453: {
454: $error = error_get_last();
455:
456: if (is_array($error)) {
457: if (__env() == ENV_PROD || error_reporting() == 0) {
458: _log($error);
459: }
460:
461: __kernelErrorHandler($error['type'], $error['message'], $error['file'], $error['line']);
462: }
463: }
464:
465: /**
466: * @internal
467: * @ignore
468: *
469: * Get friendly error type by code
470: * @param integer $code Error code
471: * @return string The friendly error type
472: */
473: function __kernelErrorTypes($code)
474: {
475: switch($code) {
476: case E_ERROR: # 1
477: return 'E_ERROR: Fatal error';
478:
479: case E_WARNING: # 2
480: return 'E_WARNING: Warning';
481:
482: case E_PARSE: # 4
483: return 'E_PARSE: Parse error';
484:
485: case E_NOTICE: # 8
486: return 'E_NOTICE: Notice';
487:
488: case E_CORE_ERROR: # 16
489: return 'E_CORE_ERROR: Fatal error';
490:
491: case E_CORE_WARNING: # 32
492: return 'E_CORE_WARNING: Warning';
493:
494: case E_COMPILE_ERROR: # 64
495: return 'E_COMPILE_ERROR: Fatal error';
496:
497: case E_COMPILE_WARNING: # 128
498: return 'E_COMPILE_WARNING: Warning';
499:
500: case E_USER_ERROR: # 256
501: return 'E_USER_ERROR: User-generated error';
502:
503: case E_USER_WARNING: # 512
504: return 'E_USER_WARNING: User-generated warning';
505:
506: case E_USER_NOTICE: # 1024
507: return 'E_USER_NOTICE: User-generated notice';
508:
509: case E_STRICT: # 2048
510: return 'E_STRICT: Information';
511:
512: case E_RECOVERABLE_ERROR: # 4096
513: return 'E_RECOVERABLE_ERROR: Catchable fatal error';
514:
515: case E_DEPRECATED: # 8192
516: return 'E_DEPRECATED: Deprecated warning';
517:
518: case E_USER_DEPRECATED: # 16384
519: return 'E_USER_DEPRECATED: User-generated deprecated warning';
520: }
521:
522: return 'E_ERROR, Error';
523: }
524:
525: /**
526: * Autoload helper
527: * @param string|array $modules The module file name
528: */
529: function __autoloadHelper($modules)
530: {
531: $modules = is_array($modules) ? $modules : array($modules);
532: $helperDirs = _baseDirs('helpers');
533:
534: foreach ($modules as $helper) {
535: foreach ($helperDirs as $dir) {
536: $moduleFile = $dir . $helper . '_helper.php';
537: if (is_file($moduleFile) && file_exists($moduleFile)) {
538: include($moduleFile);
539: }
540: }
541: }
542: }
543:
544: /**
545: * @internal
546: * @ignore
547: *
548: * Check if db is loadable (skip db initialization upon some CLI commands execution)
549: * @return bool
550: */
551: function __dbLoadable()
552: {
553: global $argv;
554:
555: return !(PHP_SAPI == 'cli'
556: && stripos($argv[0], 'lucidframe') !== false
557: && isset($argv[1]) && in_array($argv[1], ['list', 'env', 'secret:generate']));
558: }
559:
560: /**
561: * @internal
562: * @ignore
563: *
564: * Check if session is loadable (skip session initialization upon some CLI commands execution)
565: * @return bool
566: */
567: function __sessionLoadable()
568: {
569: return PHP_SAPI != 'cli';
570: }
571:
572: /*************************/
573: /* Public functions here */
574: /*************************/
575:
576: /**
577: * Get schema definition file
578: * @param string $dbNamespace The namespace for the database
579: * @param boolean $cache TRUE to look for the file in /db/build/; FALSE in /db/
580: * `TRUE` to look for the file in such priority
581: * 1. /db/build/schema.{namespace}.lock
582: * 2. /db/build/schema.lock (only for default)
583: * 3. /db/schema.{namespace}.php
584: * 4. /db/schema.php (only for default)
585: *
586: * `FALSE` to look in this priority
587: * 1. /db/schema.{namespace}.php
588: * 2. /db/schema.php (only for default)
589: *
590: * @return mixed
591: * array The schema definition
592: * null Incorrect schema definition
593: * boolean False when the file doesn't exist
594: */
595: function _schema($dbNamespace = 'default', $cache = false)
596: {
597: $files = array();
598: if ($cache) {
599: $files[] = SchemaManager::getSchemaLockFileName($dbNamespace);
600: $files[] = SchemaManager::getSchemaLockFileName();
601: }
602:
603: $files[] = DB."schema.{$dbNamespace}.php";
604: $files[] = DB."schema.php";
605:
606: foreach ($files as $f) {
607: if (is_file($f) && file_exists($f)) {
608: $file = $f;
609: if (pathinfo($file, PATHINFO_EXTENSION) == 'lock') {
610: return unserialize(file_get_contents($file));
611: } else {
612: $schema = include($file);
613: return is_array($schema) ? $schema : null;
614: }
615: }
616: }
617:
618: return false;
619: }
620:
621: /**
622: * File include helper
623: * Find files under the default directories inc/, js/, css/ according to the defined site directories $lc_sites
624: *
625: * @param $file string File name with directory path
626: * @param $recursive boolean True to find the file name until the site root
627: *
628: * @return string File name with absolute path if it is found, otherwise return an empty string
629: */
630: function _i($file, $recursive = true)
631: {
632: global $lc_baseURL;
633: global $lc_sites;
634: global $lc_languages;
635:
636: $ext = strtolower(substr($file, strrpos($file, '.')+1)); # get the file extension
637: if (in_array($ext, array('js', 'css'))) {
638: $appRoot = WEB_APP_ROOT;
639: $root = WEB_ROOT;
640: } else {
641: $appRoot = APP_ROOT;
642: $root = ROOT;
643: }
644:
645: if (!is_array($lc_languages)) {
646: $lc_languages = array('en' => 'English');
647: }
648:
649: $REQUEST_URI = $_SERVER['REQUEST_URI'];
650:
651: $requestURI = trim(ltrim($REQUEST_URI, '/'.$lc_baseURL)); # /base-dir/path/to/sub/dir to path/to/sub/dir
652: $request = explode('/', $requestURI);
653:
654: $needle = $request[0];
655: # Clean lang code in URL
656: if (array_key_exists($needle, $lc_languages)) {
657: array_shift($request);
658: }
659:
660: $folders = array();
661: if (LC_NAMESPACE == '') {
662: # Find in APP_ROOT -> ROOT
663: $folders = array(
664: APP_ROOT => $appRoot,
665: ROOT => $root
666: );
667: }
668:
669: if (isset($lc_sites) && is_array($lc_sites) && count($lc_sites)) {
670: if (array_key_exists(LC_NAMESPACE, $lc_sites)) {
671: # Find in SUB-DIR -> APP_ROOT -> ROOT
672: $folders = array(
673: APP_ROOT.$lc_sites[LC_NAMESPACE]._DS_ => $appRoot . $lc_sites[LC_NAMESPACE] . _DS_,
674: APP_ROOT => $appRoot,
675: ROOT => $root
676: );
677: }
678: }
679:
680: # $key is for file_exists()
681: # $value is for include() or <script> or <link>
682: foreach ($folders as $key => $value) {
683: if ($key === ROOT && substr($file, 0, 7) === 'helpers') {
684: $fileWithPath = LIB . $file;
685: $libHelper = true;
686: } else {
687: $fileWithPath = $key . $file;
688: $libHelper = false;
689: }
690:
691: if (is_file($fileWithPath) && file_exists($fileWithPath)) {
692: if ($libHelper === false) {
693: $fileWithPath = $value . $file;
694: }
695:
696: return $fileWithPath;
697: }
698:
699: if ($recursive == false) {
700: break;
701: }
702: }
703:
704: if (strstr($_SERVER['PHP_SELF'], APP_DIR)) {
705: if ($recursive == true) {
706: if ($root === ROOT && substr($file, 0, 7) === 'helpers') {
707: $file = LIB . $file;
708: } else {
709: $file = $root . $file;
710: }
711: } else {
712: $file = $root . $file;
713: }
714:
715: if (is_file($file) && file_exists($file)) {
716: return $file;
717: }
718: }
719:
720: return '';
721: }
722:
723: /**
724: * Get the host name or server name
725: * @return mixed|string
726: */
727: function _host()
728: {
729: if (isset($_SERVER['HTTP_HOST'])) {
730: return $_SERVER['HTTP_HOST'];
731: }
732:
733: if (isset($_SERVER['SERVER_NAME'])) {
734: return $_SERVER['SERVER_NAME'];
735: }
736:
737: return _env('host');
738: }
739:
740: /**
741: * Convenience method to get/set a config variable without global declaration within the calling function
742: *
743: * @param string $key The config variable name without prefix
744: * @param mixed $value The value to set to the config variable; if it is omitted, it is Getter method.
745: * @return mixed The value of the config variable
746: */
747: function _cfg($key, $value = '')
748: {
749: if (strrpos($key, 'lc_') === 0) {
750: $key = substr($key, 3);
751: }
752:
753: $key = 'lc_' . $key;
754:
755: return count(func_get_args()) == 2 ? __dotNotationToArray($key, 'global', $value) : __dotNotationToArray($key, 'global');
756: }
757:
758: /**
759: * Convenience method to get the value of the array config variable by its key
760: *
761: * @param string $name The config array variable name without prefix
762: * @param string $key The key of the config array of which value to be retrieved
763: * @return mixed|string|null The value of a single column of the config array variable
764: */
765: function _cfgOption($name, $key)
766: {
767: $config = _cfg($name);
768:
769: return isset($config[$key]) ? $config[$key] : null;
770: }
771:
772: /**
773: * Get the parameter value by name defined in `/inc/parameter/(development|production|staging|test).php`
774: * @param string $name The parameter name defined as key in `/inc/parameter/(development|production|staging|test).php`.
775: * The file development, production, staging or test will be determined according to the value from `.lcenv`.
776: * If `$name` is `env` (by default), it returns the current environment setting from `.lcenv`.
777: * @return mixed The value defined `/inc/parameter/(development|production|staging|test).php`
778: */
779: function _p($name = 'env')
780: {
781: if ($name == 'env') {
782: return __env();
783: }
784:
785: global $argv;
786:
787: if (PHP_SAPI == 'cli' && isset($argv[0]) && stripos($argv[0], 'lucidframe') !== false) {
788: # keep the current environment when `php lucidframe` is run
789: $env = _cfg('env');
790: } elseif (PHP_SAPI == 'cli' || stripos($_SERVER['REQUEST_URI'], 'tests/') !== false) {
791: # force change to "test" environment when run `php tests/tests.php` from CLI
792: # or when run `/tests/tests.php` from browser
793: $env = 'test';
794: _cfg('env', $env);
795: } else {
796: # neither CLI nor test
797: $env = _cfg('env');
798: }
799:
800: if (!in_array($env, __envList())) {
801: die(sprintf('Wrong environment configuration. Use "%s" or "%s" or "%s" or "%s".', ENV_DEV, ENV_STAGING, ENV_PROD, ENV_TEST));
802: }
803:
804: $param = include(INC . 'parameter/' . $env . '.php');
805:
806: return __dotNotationToArray($name, $param);
807: }
808:
809: /**
810: * Convenience method to get/set a global variable
811: *
812: * @param string $key The global variable name
813: * @param mixed $value The value to set to the global variable; if it is not given, it is Getter method.
814: * @return mixed The value of the global variable
815: */
816: function _g($key, $value = '')
817: {
818: if (empty($key)) {
819: return null;
820: }
821:
822: if (count(func_get_args()) == 2) {
823: return __dotNotationToArray($key, 'global', $value);
824: } else {
825: return __dotNotationToArray($key);
826: }
827: }
828:
829: /**
830: * Get the parameter value by name defined in `/inc/parameter/env.inc`
831: * @param string $name The parameter name in dot annotation format such as `prod.db.default.database`
832: * @param mixed $default The default value if the parameter name doesn't exist
833: * @return mixed The value defined in `/inc/parameter/env.inc`
834: */
835: function _env($name, $default = '')
836: {
837: global $lc_envParameters;
838:
839: if ($lc_envParameters === null) {
840: $files = array(
841: INC . 'parameter/env.inc',
842: INC . 'parameter/parameter.env.inc',
843: );
844:
845: foreach ($files as $file) {
846: if (is_file($file) && file_exists($file)) {
847: $lc_envParameters = include($file);
848: break;
849: }
850: }
851: }
852:
853: $value = __dotNotationToArray($name, $lc_envParameters);
854:
855: return $value ?: $default;
856: }
857:
858: /**
859: * Get base URL with protocol
860: * @return string
861: */
862: function _baseUrlWithProtocol()
863: {
864: $baseUrl = _cfg('baseURL');
865: $protocol = _cfg('ssl') ? 'https' : 'http';
866:
867: if (PHP_SAPI == 'cli') {
868: $base = trim(_p('siteDomain'), '/');
869: } else {
870: $base = strtolower($protocol) . '://';
871: $base .= $_SERVER['HTTP_HOST'];
872: }
873:
874: if ($baseUrl) {
875: $base .= '/' . $baseUrl;
876: }
877:
878: return $base;
879: }
880:
881: /**
882: * Get base directory list by priority
883: * @param string $subDir The subdirectory name
884: * @return string[]
885: */
886: function _baseDirs($subDir = '')
887: {
888: $folders = array();
889:
890: $namespace = LC_NAMESPACE;
891: if (!empty($_GET['lc_namespace'])) {
892: $namespace = $_GET['lc_namespace'];
893: }
894:
895: $sites = _cfg('sites');
896: if (count($sites) && array_key_exists($namespace, $sites)) {
897: $folders[] = rtrim(APP_ROOT . $sites[$namespace] . _DS_ . $subDir, _DS_) . _DS_;
898: }
899:
900: $folders[] = rtrim(APP_ROOT . $subDir, _DS_) . _DS_;
901: $folders[] = rtrim(LIB . $subDir, _DS_) . _DS_;
902:
903: return $folders;
904: }
905:
906: /**
907: * Write log to file
908: *
909: * @param array|string $msg The message to log
910: * @param string $file The destination file name
911: * @param int $type 0 ~ 4 See error_log() at https://www.php.net/manual/en/function.error-log
912: * @return bool
913: */
914: function _log($msg, $file = '', $type = 3)
915: {
916: if ($file) {
917: $file = $file . '-'. date('Ymd') . '.log';
918: } else {
919: $file = 'log-'. date('Ymd') . '.log';
920: }
921: $file = LOG . $file;
922: $file = substr($file, 0, strrpos($file, '.log'));
923: $file .= '-' . __env();
924: if (strtolower(php_sapi_name()) == 'cli') {
925: $file .= '.cli';
926: }
927: $file .= '.log';
928:
929: if (is_array($msg)) {
930: if (isset($msg['message'])) {
931: $msg = $msg['message'] . ' in ' . $msg['file'] . ' on line ' . $msg['line'];
932: } else {
933: $msg = print_r($msg, true);
934: }
935: }
936:
937: $msg = '[' . date('Y-m-d H:i:s') . '] : ' . trim($msg) . PHP_EOL;
938:
939: return error_log($msg, $type, $file);
940: }
941:
942:
943: __prerequisite();
944: