1: <?php
2: /**
3: * This file is part of the PHPLucidFrame library.
4: * Core utility for general purpose functions.
5: *
6: * @package PHPLucidFrame\Core
7: * @since PHPLucidFrame v 1.0.0
8: * @copyright Copyright (c), PHPLucidFrame.
9: * @link http://phplucidframe.com
10: * @license http://www.opensource.org/licenses/mit-license.php MIT License
11: *
12: * This source file is subject to the MIT license that is bundled
13: * with this source code in the file LICENSE
14: */
15:
16: use LucidFrame\Console\Command;
17: use LucidFrame\Console\Console;
18: use LucidFrame\Console\ConsoleTable;
19: use LucidFrame\Core\Form;
20: use LucidFrame\Core\Middleware;
21: use LucidFrame\Core\Pager;
22: use LucidFrame\Core\AsynFileUploader;
23: use LucidFrame\Core\File;
24: use LucidFrame\Core\SchemaManager;
25: use LucidFrame\Core\App;
26:
27: /**
28: * Set/Get a global variable/object
29: * @param string $name The name of the variable/object
30: * @param mixed $value The value or the variable or object
31: * @return mixed The value stored globally
32: */
33: function _app($name, $value = null)
34: {
35: if (count(func_get_args()) == 2) {
36: return App::$$name = $value;
37: } else {
38: return App::$$name;
39: }
40: }
41:
42: /**
43: * Returns the current PHPLucidFrame version
44: * @return string
45: */
46: function _version()
47: {
48: $findIn = array(LIB, ROOT);
49: foreach ($findIn as $dir) {
50: $versionFile = $dir . 'VERSION';
51: if (is_file($versionFile) && file_exists($versionFile)) {
52: return trim(file_get_contents($versionFile));
53: }
54: }
55:
56: return 'Unknown';
57: }
58:
59: /**
60: * @internal
61: * @ignore
62: *
63: * ob_start callback function to output buffer
64: * It also adds the following to <html>
65: *
66: * - a class for IE "ie ieX"
67: * - lang="xx" for multilingual site
68: * - itemscope and itemtype attribute
69: *
70: * Hook to implement `__flush()` at app/helpers/utility_helper.php
71: *
72: * @param string $buffer Contents of the output buffer.
73: * @param int $phase Bitmask of PHP_OUTPUT_HANDLER_* constants.
74: *
75: * PHP_OUTPUT_HANDLER_CLEANABLE
76: * PHP_OUTPUT_HANDLER_FLUSHABLE
77: * PHP_OUTPUT_HANDLER_REMOVABLE
78: *
79: * @return string
80: * @see php.net/ob_start
81: */
82: function _flush($buffer, $phase)
83: {
84: if (function_exists('__flush')) {
85: $buffer = __flush($buffer, $phase); # Run the hook if any
86: } else {
87: $posHtml = stripos($buffer, '<html');
88: $posHead = stripos($buffer, '<head');
89:
90: $beforeHtmlTag = substr($buffer, 0, $posHtml);
91: $afterHtmlTag = substr($buffer, $posHead);
92: $htmlTag = trim(str_ireplace($beforeHtmlTag, '', substr($buffer, 0, $posHead)));
93:
94: if (trim($htmlTag)) {
95: $htmlTag = trim(ltrim($htmlTag, '<html.<HTML'), '>. ');
96: $attributes = array();
97: $attrList = explode(' ', $htmlTag);
98: foreach ($attrList as $list) {
99: $attr = explode('=', $list);
100: $attr[0] = trim($attr[0]);
101: if (count($attr) == 2) {
102: $attr[1] = trim($attr[1], '".\'');
103: }
104: $attributes[$attr[0]] = $attr;
105: }
106:
107: $IE = false;
108: $IEVersion = '';
109: $userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
110: if (strpos($userAgent, 'MSIE') !== false || strpos($userAgent, 'Trident') !== false) {
111: $IE = true;
112: if (preg_match('/(MSIE|rv)\s(\d+)/i', $userAgent, $m)) {
113: $IEVersion = 'ie' . $m[2];
114: }
115: }
116:
117: if (array_key_exists('class', $attributes)) {
118: # if there is class attribute provided
119: if ($IE) {
120: $attributes['class'][1] .= ' ie ' . $IEVersion;
121: }
122: if (_multilingual()) {
123: $attributes['class'][1] .= ' ' . _lang();
124: }
125: } else {
126: $translationEnabled = _cfg('translationEnabled');
127: # if there is no class attributes provided
128: if ($IE || _multilingual() || $translationEnabled) {
129: $value = array();
130: if ($IE) {
131: $value[] = 'ie ' . $IEVersion; # ie class
132: }
133: if (_multilingual() || $translationEnabled) {
134: $value[] = _lang(); # lang class
135: }
136: if (count($value)) {
137: $attributes['class'] = array('class', implode(' ', $value));
138: }
139: }
140: }
141:
142: if (_multilingual()) {
143: # lang attributes
144: if (!array_key_exists('lang', $attributes)) {
145: # if there is no lang attribute provided
146: $attributes['lang'] = array('lang', _lang());
147: }
148: }
149:
150: if (!array_key_exists('itemscope', $attributes)) {
151: $attributes['itemscope'] = array('itemscope');
152: }
153:
154: if (!array_key_exists('itemtype', $attributes)) {
155: # if there is no itemtype attribute provided
156: # default to "WebPage"
157: $attributes['itemtype'] = array('itemtype', "http://schema.org/WebPage");
158: }
159:
160: ksort($attributes);
161: $html = '<html';
162: foreach ($attributes as $key => $value) {
163: $html .= ' '.$key;
164: if (isset($value[1])) {
165: # some attribute may not have value, such as itemscope
166: $html .= '="' . $value[1] . '"';
167: }
168: }
169: $html .= '>' . "\r\n";
170: $buffer = $beforeHtmlTag . $html . $afterHtmlTag;
171: }
172: }
173:
174: # compress the output
175: $buffer = _minifyHTML($buffer);
176:
177: $posDoc = stripos($buffer, '<!DOCTYPE');
178:
179: return substr($buffer, $posDoc);
180: }
181:
182: /**
183: * Minify and compress the given HTML according to the configuration `$lc_minifyHTML`
184: * @param string $html HTML to be compressed or minified
185: * @return string The compressed or minifed HTML
186: */
187: function _minifyHTML($html)
188: {
189: if (_cfg('minifyHTML')) {
190: # 1. strip whitespaces after tags, except space
191: # 2. strip whitespaces before tags, except space
192: # 3. shorten multiple whitespace sequences
193: return preg_replace(array('/\>[^\S ]+/s', '/[^\S ]+\</s', '/(\s)+/s'), array('>', '<', '\\1'), $html);
194: }
195:
196: return $html;
197: }
198:
199: /**
200: * Get a full path directory
201: * @param string $name The short code for directory
202: * @return string Full path directory
203: */
204: function _dir($name)
205: {
206: switch($name) {
207: case 'inc':
208: return ROOT . 'inc' . _DS_;
209: case 'db':
210: return ROOT . 'db' . _DS_;
211: case 'lib':
212: return ROOT . 'lib' . _DS_;
213: case 'helper':
214: return ROOT . 'lib' . _DS_ . 'helpers' . _DS_;
215: case 'class':
216: return ROOT . 'lib' . _DS_ . 'classes' . _DS_;
217: case 'i18n':
218: return ROOT . 'i18n' . _DS_;
219: case 'vendor':
220: return ROOT . 'vendor' . _DS_;
221: case 'business':
222: return ROOT . 'business' . _DS_;
223: case 'asset':
224: return ROOT . 'assets' . _DS_;
225: case 'image':
226: return ROOT . 'assets' . _DS_ . 'images' . _DS_;
227: case 'file':
228: return ROOT . 'files' . _DS_;
229: case 'cache':
230: return ROOT . 'files' . _DS_ . 'cache' . _DS_;
231: case 'test':
232: return ROOT . 'tests' . _DS_;
233: }
234:
235: return ROOT;
236: }
237:
238: /**
239: * Auto-load a library, script or file
240: * @param string $name The file name without extension
241: * @param string $path The directory path for the library, script or file; default to helpers/
242: * @return void
243: */
244: function _loader($name, $path = HELPER)
245: {
246: global $lc_autoload;
247:
248: $path = rtrim($path, _DS_) . _DS_;
249:
250: $dir = $path . $name . _DS_;
251: if (is_dir($dir)) {
252: // include all files from the library
253: $files = scandir($dir);
254: foreach ($files as $fileName) {
255: $dir = rtrim(rtrim($dir, '/'), '\\');
256: $file = $dir . _DS_ . $fileName;
257:
258: if (!in_array(substr($fileName, -3), array('php', 'inc')) || !is_file($file)) {
259: continue;
260: }
261:
262: if (file_exists($file)) {
263: $lc_autoload[] = $file;
264: }
265: }
266: } else {
267: // include one file from the library
268: $name = rtrim($name, '.php');
269: $lc_autoload[] = $path . $name . '.php';
270: }
271:
272: $lc_autoload = array_unique($lc_autoload);
273: }
274:
275: /**
276: * Removing a library, script or file from auto-load
277: * @param string $name The file name without extension
278: * @param string $path The directory path for the library, script or file; default to helpers/
279: * @return void
280: */
281: function _unloader($name, $path = HELPER)
282: {
283: global $lc_autoload;
284:
285: $file = $path . $name . '.php';
286: $key = array_search($file, $lc_autoload);
287: if ($key !== false) {
288: unset($lc_autoload[$key]);
289: $lc_autoload = array_values($lc_autoload);
290: }
291: }
292:
293: /**
294: * @internal
295: * @ignore
296: *
297: * Check a library, script or file is ready to load
298: * @param string $name The file name without extension
299: * @param string $path The directory path for the library, script or file; default to helpers/
300: * @return mixed The file name if it is ready to load, otherwise FALSE
301: */
302: function _readyloader($name, $path = HELPER)
303: {
304: global $lc_autoload;
305:
306: if (stripos($name, '.php') === false) {
307: $file = $path . $name . '.php';
308: } else {
309: $file = $name;
310: }
311:
312: return (array_search($file, $lc_autoload) !== false && is_file($file) && file_exists($file)) ? $file : false;
313: }
314:
315: /**
316: * Autoload classes from directory
317: * @param string $dir Directory path from which all files to be included
318: * @param string $scope The sub-site scope/namespace (if it is given, the directory will only be loaded under that scope)
319: * @return void
320: */
321: function _autoloadDir($dir, $scope = '')
322: {
323: $scope = trim($scope, '/');
324:
325: if (is_dir($dir)) {
326: $files = scandir($dir);
327: foreach ($files as $fileName) {
328: if (in_array($fileName, array('.', '..', '.gitkeep', 'README.md', 'empty'))) {
329: continue;
330: }
331:
332: $dir = rtrim(rtrim($dir, '/'), '\\');
333: $file = $dir . _DS_ . $fileName;
334:
335: if (is_dir($file)) {
336: _autoloadDir($file, $scope);
337: break;
338: }
339:
340: if (!in_array(substr($fileName, -3), array('php', 'inc')) || !is_file($file)) {
341: continue;
342: }
343:
344: if (file_exists($file) && (empty($scope) || $scope == LC_NAMESPACE)) {
345: require_once $file;
346: }
347: }
348: }
349: }
350:
351: /**
352: * Declare global JS variables
353: * Hook to implement `__script()` at app/helpers/utility_helper.php
354: *
355: * @return void
356: */
357: function _script()
358: {
359: $sitewideWarnings = _cfg('sitewideWarnings');
360: $sites = _cfg('sites');
361: $script = '<script type="text/javascript">';
362: $script .= 'var LC = {};';
363: if (WEB_ROOT) {
364: $script .= 'var WEB_ROOT = "'.WEB_ROOT.'";';
365: $script .= 'LC.root = WEB_ROOT;';
366: }
367: if (WEB_APP_ROOT) {
368: $script .= 'var WEB_APP_ROOT = "'.WEB_APP_ROOT.'";';
369: $script .= 'LC.appRoot = WEB_ROOT;';
370: }
371: $script .= 'LC.env = "' . __env() . '";';
372: $script .= 'LC.self = "'._self().'";';
373: $script .= 'LC.lang = "'._lang().'";';
374: $script .= 'LC.baseURL = "'._cfg('baseURL').'/";';
375: $script .= 'LC.route = "'._r().'";';
376: $script .= 'LC.cleanRoute = "'._cfg('cleanRoute').'";';
377: $script .= 'LC.namespace = "'.LC_NAMESPACE.'";';
378: $script .= 'LC.sites = "' . base64_encode(is_array($sites) && count($sites) ? json_encode($sites) : '[]') . _randomCode() . '";';
379: $script .= 'LC.sitewideWarnings = '.json_encode($sitewideWarnings).';';
380:
381: $csrf = ['name' => _cfg('csrfHeaderTokenName'), 'value' => _encrypt(Form::$formToken)];
382: $script .= 'LC.csrfToken = ' . json_encode($csrf) . ';';
383:
384: # run hook
385: # @deprecated __script() hook will be removed in the later version
386: if (function_exists('__script')) {
387: __script();
388: }
389:
390: # user defined variables
391: $jsVars = _cfg('jsVars');
392: $script .= 'LC.vars = {};';
393: $script .= 'LC.vars.baseDir = "' . _cfg('baseDir') . '";';
394: if (count($jsVars)) {
395: foreach ($jsVars as $name => $val) {
396: if (is_object($val)) {
397: $val = (array) $val;
398: }
399:
400: if (is_array($val)) {
401: $script .= 'LC.vars.'.$name.' = '.json_encode($val).';';
402: } elseif (is_numeric($val)) {
403: $script .= 'LC.vars.'.$name.' = '.$val.';';
404: } else {
405: $script .= 'LC.vars.'.$name.' = "'.$val.'";';
406: }
407: }
408: }
409: $script .= '</script>';
410: echo $script;
411: }
412:
413: /**
414: * Passing values from PHP to Javascript making available to `LC.vars`
415: * @param string $name The JS variable name
416: * @param mixed $value The value for the JS variable
417: */
418: function _addJsVar($name, $value = '')
419: {
420: global $lc_jsVars;
421: $lc_jsVars[$name] = $value;
422: }
423:
424: /**
425: * JS file include helper
426: *
427: * @param string $file An absolute file path or just file name.
428: * The file name only will be prepended the folder name js/ and it will be looked in every sub-sites "js" folder
429: * @param string $subDir The sub-directory under assets directory, where the file exists
430: * @param bool $return [optional] If you would like to capture the output of _js, use the return parameter.
431: * If this parameter is set to true, _js will return its output, instead of printing it (which it does by default).
432: * @return boolean Return true if the file found and included, otherwise false
433: */
434: function _js($file, $subDir = '', $return = false)
435: {
436: $version = '?v' . _cfg('assetVersion');
437:
438: if (stripos($file, 'http') === 0 || stripos($file, '//') === 0) {
439: $html = '<script src="' . $file . '" type="text/javascript"></script>';
440: if ($return) {
441: return $html;
442: }
443:
444: echo $html;
445: return true;
446: }
447:
448: if ($subDir) {
449: $subDir = trim('assets/' . $subDir, '/') . '/';
450: } else {
451: $subDir = 'assets/js/';
452: }
453:
454: $includeFiles = array();
455: if ($file == 'jquery.ui' || $file == 'jquery-ui') {
456: # jQuery UI
457: $file = (stripos($file, '.js') !== false) ? $file : 'jquery-ui.min.js';
458: $includeFiles[] = $subDir . 'vendor/jquery-ui/' . $file;
459: } elseif ($file == 'jquery') {
460: # jQuery
461: $file = (stripos($file, '.js') !== false) ? $file : 'jquery.min.js';
462: $includeFiles[] = $subDir . 'vendor/jquery/' . $file;
463: } else {
464: # Other files
465: $includeFiles[] = $subDir . $file;
466: }
467:
468: foreach ($includeFiles as $includeFile) {
469: $includeFile = _i($includeFile);
470: if (stripos($includeFile, 'http') === 0) {
471: if (stripos($includeFile, WEB_APP_ROOT) === 0) {
472: $fileWithSystemPath = APP_ROOT . str_replace(WEB_APP_ROOT, '', $includeFile);
473: } else {
474: $fileWithSystemPath = ROOT . str_replace(WEB_ROOT, '', $includeFile);
475: }
476: if (file_exists($fileWithSystemPath)) {
477: $html = '<script src="' . $includeFile . $version . '" type="text/javascript"></script>';
478: if ($return) {
479: return $html;
480: }
481:
482: echo $html;
483: return true;
484: }
485: }
486: }
487:
488: return false;
489: }
490:
491: /**
492: * CSS file include helper
493: *
494: * @param string $file An absolute file path or file name only.
495: * The file name only will be prepended the folder name css/ and it will be looked in every sub-sites "css" folder
496: * @param string $subDir The sub-directory under assets directory, where the file exists
497: * @param bool $return [optional] If you would like to capture the output of _js, use the return parameter.
498: * If this parameter is set to true, _js will return its output, instead of printing it (which it does by default).
499: * @return boolean Return true if the file found and included, otherwise false
500: */
501: function _css($file, $subDir = '', $return = false)
502: {
503: $version = '?v' . _cfg('assetVersion');
504:
505: if (stripos($file, 'http') === 0 || stripos($file, '//') === 0) {
506: $html = '<link href="' . $file . '" rel="stylesheet" type="text/css" />';
507: if ($return) {
508: return $html;
509: }
510:
511: echo $html;
512: return true;
513: }
514:
515: if ($subDir) {
516: $subDir = trim('assets/' . $subDir, '/') . '/';
517: } else {
518: $subDir = 'assets/css/';
519: }
520:
521: $includeFiles = array();
522: if ($file == 'jquery.ui' || $file == 'jquery-ui') {
523: # jQuery UI
524: $includeFiles[] = 'assets/js/vendor/jquery-ui/jquery-ui.min.css';
525: } else {
526: # Other files
527: $includeFiles[] = $subDir . $file;
528: }
529:
530: foreach ($includeFiles as $includeFile) {
531: $includeFile = _i($includeFile);
532: if (stripos($includeFile, 'http') === 0) {
533: if (stripos($includeFile, WEB_APP_ROOT) === 0) {
534: $fileWithSystemPath = APP_ROOT . str_replace(WEB_APP_ROOT, '', $includeFile);
535: } else {
536: $fileWithSystemPath = ROOT . str_replace(WEB_ROOT, '', $includeFile);
537: }
538:
539: if (file_exists($fileWithSystemPath)) {
540: $html = '<link href="' . $includeFile . $version . '" rel="stylesheet" type="text/css" />';
541: if ($return) {
542: return $html;
543: }
544:
545: echo $html;
546: return true;
547: }
548: }
549: }
550:
551: return false;
552: }
553:
554: /**
555: * Get the image file name with absolute web path
556: *
557: * @param string $file An image file name only (no need directory path)
558: * @param bool $version Whether asset version appended to the file name or not
559: * @return string The absolute image URL if the file found or empty string if it is not found
560: */
561: function _img($file, $version = null)
562: {
563: $fileWithPath = 'assets/images/' . $file;
564: $fileWithPath = _i($fileWithPath);
565:
566: if (empty($fileWithPath)) {
567: return '';
568: }
569:
570: if (stripos($fileWithPath, APP_ROOT) === 0) {
571: $img = WEB_APP_ROOT . str_replace(APP_ROOT, '', $fileWithPath);
572: } else {
573: $img = WEB_ROOT . str_replace(ROOT, '', $fileWithPath);
574: }
575:
576: if ($version) {
577: $img .= '?v' . _cfg('assetVersion');
578: }
579:
580: return $img;
581: }
582:
583: if (!function_exists('_image')) {
584: /**
585: * Display an image fitting into the desired dimension
586: * It expects the file existing in one of the directories `./files` (the constant `FILE`)
587: * and `./images` (the constant `IMAGE`)
588: * This function has dependency on file_helper. If there is no file_helper found,
589: * the arguments `$dimension` and `$attributes` will be ignored.
590: *
591: * @param string $file The image file name with path excluding
592: * the base directory name (FILE or IMAGE) without leading slash.
593: * @param string $caption The image caption
594: * @param string $dimension The desired dimension in "widthxheight"
595: * @param array $attributes The HTML attributes in array like key => value
596: *
597: * @return void
598: */
599: function _image($file, $caption = '', $dimension = '0x0', array $attributes = array())
600: {
601: $directory = array(
602: 'images' => IMAGE,
603: 'files' => FILE,
604: );
605: # find the image in the two directories - ./files and ./images
606: foreach ($directory as $dir => $path) {
607: $image = $path . $file;
608: if (is_file($image) && file_exists($image)) {
609: list($width, $height) = getimagesize($image);
610: if (strpos($path, 'assets') !== false) {
611: $dir = 'assets/images';
612: }
613: break;
614: }
615: }
616: if (isset($width) && isset($height)) {
617: # if the image is found
618: $image = WEB_ROOT . $dir . '/' . $file;
619: echo File::img($image, $caption, $width.'x'.$height, $dimension, $attributes);
620: } else {
621: # if the image is not found
622: echo '<div class="image404" align="center">';
623: echo function_exists('_t') ? _t('No Image') : 'No Image';
624: echo '</div>';
625: }
626: }
627: }
628:
629: if (!function_exists('_pr')) {
630: /**
631: * Convenience method for `print_r`.
632: * Displays information about a variable in a way that's readable by humans.
633: * If given a string, integer or float, the value itself will be printed.
634: * If given an array, values will be presented in a format that shows keys and elements.
635: *
636: * @param mixed $input The variable to debug
637: * @param boolean $pre TRUE to print using `<pre>`, otherwise FALSE
638: *
639: * @return void
640: */
641: function _pr($input, $pre = true)
642: {
643: if ($pre) {
644: echo '<pre>';
645: }
646: if (is_array($input) || is_object($input)) {
647: print_r($input);
648: } else {
649: if (is_bool($input)) {
650: var_dump($input);
651: } else {
652: echo $input;
653: }
654: if ($pre == false) {
655: echo '<br>';
656: }
657: }
658: if ($pre) {
659: echo '</pre>';
660: }
661: }
662: }
663:
664: if (!function_exists('_dpr')) {
665: /**
666: * Convenience method for `print_r` + `die`.
667: * Displays information about a variable in a way that's readable by humans.
668: * If given a string, integer or float, the value itself will be printed.
669: * If given an array, values will be presented in a format that shows keys and elements.
670: *
671: * @param mixed $input The variable to debug
672: * @param boolean $pre TRUE to print using `<pre>`, otherwise FALSE
673: *
674: * @return void
675: */
676: function _dpr($input, $pre = true)
677: {
678: _pr($input);
679: exit;
680: }
681: }
682:
683: if (!function_exists('_dump')) {
684: /**
685: * Convenience method for `var_dump`.
686: * Dumps information about a variable
687: *
688: * @param mixed $input mixed The variable to debug
689: * @param boolean $pre boolean TRUE to print using `<pre>`, otherwise FALSE
690: *
691: * @return void
692: */
693: function _dump($input, $pre = true)
694: {
695: if ($pre) {
696: echo '<pre>';
697: }
698: var_dump($input);
699: if ($pre) {
700: echo '</pre>';
701: }
702: }
703: }
704:
705: /**
706: * Convenience method for htmlspecialchars.
707: *
708: * @param string $string The string being converted
709: * @return string The converted string
710: */
711: function _h($string)
712: {
713: if (empty($string)) {
714: return $string;
715: }
716:
717: $string = stripslashes($string);
718: $string = htmlspecialchars_decode($string, ENT_QUOTES);
719:
720: return htmlspecialchars($string, ENT_QUOTES); # ENT_QUOTES will convert both double and single quotes.
721: }
722:
723: /**
724: * Get the current site language code
725: * @return string The language code
726: */
727: function _lang()
728: {
729: return _cfg('lang');
730: }
731:
732: /**
733: * Get the language to process
734: * Read "lang" from query string; if it is not found, get the default language code
735: * Basically, it is useful for admin content management by language
736: * Hook to implement `__getLang()` at app/helpers/utility_helper.php
737: *
738: * @return string The language code
739: */
740: function _getLang()
741: {
742: if (function_exists('__getLang')) {
743: return __getLang(); # run the hook if any
744: }
745:
746: $lang = (_arg('lang')) ? _arg('lang') : _defaultLang();
747:
748: return ($lang) ? $lang : _defaultLang();
749: }
750:
751: /**
752: * Get the default site language code
753: * @return string The default site language code
754: */
755: function _defaultLang()
756: {
757: return _cfg('defaultLang');
758: }
759:
760: /**
761: * Get array of the defined languages
762: * @param string|array $excepts The exceptional langauges to exclude
763: * @return array|boolean The filtered language array or FALSE for no multi-language
764: */
765: function _langs($excepts = null)
766: {
767: global $lc_languages;
768:
769: $langs = array();
770: if ($excepts) {
771: foreach ($lc_languages as $lcode => $lname) {
772: if (is_array($excepts) && in_array($lcode, $excepts)) {
773: continue;
774: }
775: if (is_string($excepts) && $lcode == $excepts) {
776: continue;
777: }
778: $langs[$lcode] = $lname;
779: }
780: } else {
781: $langs = $lc_languages;
782: }
783:
784: return count($langs) ? $langs : false;
785: }
786:
787: /**
788: * Get the current site language code by converting dash (URL-friendly) to underscore (db-friendly)
789: * @param string $lang The language code (optional - if not provided, the current language code will be used)
790: * @return string The language code
791: */
792: function _queryLang($lang = null)
793: {
794: if (!$lang) {
795: $lang = _cfg('lang');;
796: }
797:
798: return str_replace('-', '_', $lang);
799: }
800:
801: /**
802: * Get the current site language code by converting underscore (db-friendly) to dash (URL-friendly)
803: * @param string $lang The language code (optional - if not provided, the current language code will be used)
804: * @return string The language code
805: */
806: function _urlLang($lang = null)
807: {
808: if (!$lang) {
809: $lang = _cfg('lang');
810: }
811:
812: return str_replace('_', '-', $lang);
813: }
814:
815: /**
816: * Get the default site language code by converting dash to underscore
817: * @return string The language code
818: */
819: function _defaultQueryLang()
820: {
821: return str_replace('-', '_', _cfg('defaultLang'));
822: }
823:
824: /**
825: * Get the current site language name of the given language code
826: * If the site is multilingual, return empty
827: * If no given code, return the language name of the default language code
828: *
829: * @param string $lang The language code (optional - if not provided,
830: * the default language code from $lc_defaultLang will be used)
831: * @return string The language name as per defined in /inc/config.php
832: */
833: function _langName($lang = '')
834: {
835: if (!_multilingual()) {
836: return '';
837: }
838:
839: global $lc_languages;
840: $lang = str_replace('_', '-', $lang);
841:
842: if (isset($lc_languages[$lang])) {
843: return $lc_languages[$lang];
844: } else {
845: return $lc_languages[_cfg('defaultLang')];
846: }
847: }
848:
849: /**
850: * Get the current site is multilingual or not
851: * @return boolean
852: */
853: function _multilingual()
854: {
855: if (_cfg('languages')) {
856: return count(_cfg('languages')) > 1;
857: } else {
858: return false;
859: }
860: }
861:
862: /**
863: * Get the server protocol
864: * For example, http, https, ftp, etc.
865: *
866: * @return string The protocol - http, https, ftp, etc.
867: */
868: function _protocol()
869: {
870: $protocol = current(explode('/', $_SERVER['SERVER_PROTOCOL']));
871:
872: return strtolower($protocol);
873: }
874:
875: /**
876: * Check SSL or not
877: *
878: * @return boolean TRUE if https otherwise FALSE
879: */
880: function _ssl()
881: {
882: return _cfg('ssl');
883: }
884:
885: /**
886: * Get the current routing path
887: * For example,
888: *
889: * - example.com/foo/bar would return foo/bar
890: * - example.com/en/foo/bar would also return foo/bar
891: * - example.com/1/this-is-slug (if accomplished by RewriteRule) would return the underlying physical path
892: *
893: * @return string The route path starting from the site root
894: */
895: function _r()
896: {
897: return route_path();
898: }
899:
900: /**
901: * The more realistic function to get the current routing path on the address bar regardless of RewriteRule behind
902: * For example,
903: *
904: * - example.com/foo/bar would return foo/bar
905: * - example.com/en/foo/bar would also return foo/bar
906: * - example.com/foo/bar?id=1 would also return foo/bar
907: * - example.com/1/this-is-slug would return 1/this-is-slug
908: *
909: * @return string The route path starting from the site root
910: */
911: function _rr()
912: {
913: if (!_isRewriteRule()) {
914: return _r();
915: }
916:
917: $uri = REQUEST_URI;
918: if (strpos($uri, '?') !== false) { // exclude query string
919: $uri = substr($uri, 0, strpos($uri, '?'));
920: }
921:
922: return $uri;
923: }
924:
925: /**
926: * Get the clean routing path without the query string
927: * For example, `example.com/post/1/edit` would return `post`
928: * @return string The route path starting from the site root
929: */
930: function _cr()
931: {
932: return _cfg('cleanRoute');
933: }
934:
935: /**
936: * Get the absolute URL path
937: * @param string $path Routing path such as "foo/bar"; null for the current path
938: * @param array $queryStr Query string as
939: *
940: * array(
941: * $value1, // no key here
942: * 'key1' => $value2,
943: * 'key3' => $value3 or array($value3, $value4)
944: * )
945: *
946: * @param string $lang Language code to be prepended to $path such as "en/foo/bar".
947: * It will be useful for site language switch redirect
948: * @return string
949: */
950: function _url($path = null, $queryStr = array(), $lang = '')
951: {
952: return route_url($path, $queryStr, $lang);
953: }
954:
955: /**
956: * Get the absolute URL path
957: * @param array $queryStr Query string as
958: *
959: * array(
960: * $value1, // no key here
961: * 'key1' => $value2,
962: * 'key3' => $value3 or array($value3, $value4)
963: * )
964: *
965: * @param string $lang Languague code to be prepended to $path such as "en/foo/bar".
966: * It will be useful for site language switch redirect
967: * @return string
968: */
969: function _self($queryStr = array(), $lang = '')
970: {
971: return route_url(null, $queryStr, $lang);
972: }
973:
974: /**
975: * Send HTTP header
976: * @param int $status The HTTP status code
977: * @param string $message Message along with status code
978: * @return void
979: */
980: function _header($status, $message = null)
981: {
982: _g('httpStatusCode', $status);
983:
984: if (PHP_SAPI != 'cli' && _cfg('env') != ENV_TEST && __env() != ENV_TEST) {
985: header('HTTP/1.1 ' . $status . ($message ? ' ' . $message : ''));
986: }
987: }
988:
989: /**
990: * Header redirect to a specific location
991: * @param string $path Routing path such as "foo/bar"; null for the current path
992: * @param array $queryStr Query string as
993: *
994: * array(
995: * $value1, // no key here
996: * 'key1' => $value2,
997: * 'key3' => $value3 or array($value3, $value4)
998: * )
999: *
1000: * @param string $lang The Language code to be prepended to $path such as "en/foo/bar".
1001: * It will be useful for site language switch redirect
1002: * @param int $status The HTTP status code
1003: * use `_redirect301()` instead; do not provide this for default 302 redirect.
1004: * @return void
1005: */
1006: function _redirect($path = null, $queryStr = array(), $lang = '', $status = null)
1007: {
1008: if ($path && stripos($path, 'http') === 0) {
1009: if ($status === 301) {
1010: _header(301, 'Moved Permanently');
1011: }
1012: header('Location: ' . $path);
1013: exit;
1014: }
1015:
1016: if ($path == 'self') {
1017: $url = _self(null, $lang);
1018: } else {
1019: $url = route_url($path, $queryStr, $lang);
1020: }
1021:
1022: if ($status === 301) {
1023: _header(301, 'Moved Permanently');
1024: }
1025:
1026: header('Location: ' . $url);
1027: exit;
1028: }
1029:
1030: /**
1031: * Header redirect to a specific location by sending 301 status code
1032: * @param string $path Routing path such as "foo/bar"; null for the current path
1033: * @param array $queryStr Query string as
1034: *
1035: * array(
1036: * $value1, // no key here
1037: * 'key1' => $value2,
1038: * 'key3' => $value3 or array($value3, $value4)
1039: * )
1040: *
1041: * @param string $lang Languague code to be prepended to $path such as "en/foo/bar".
1042: * It will be useful for site language switch redirect
1043: * @return void
1044: */
1045: function _redirect301($path = null, $queryStr = array(), $lang = '')
1046: {
1047: _redirect($path, $queryStr, $lang, 301);
1048: }
1049:
1050: /**
1051: * Display 401 page
1052: * @param string $message The error message
1053: * @return void
1054: */
1055: function _page401($message = '')
1056: {
1057: $message = $message ?: _t('Access Denied');
1058:
1059: if (_isContentType('application/json')) {
1060: _jsonError($message, '', 401);
1061: }
1062:
1063: _header(401);
1064:
1065: _cfg('layoutMode', true);
1066: include(INC . 'tpl/401.php');
1067: exit;
1068: }
1069:
1070: /**
1071: * Display 403 page
1072: * @param string $message The error message
1073: * @return void
1074: */
1075: function _page403($message = '')
1076: {
1077: $message = $message ?: _t('403 Forbidden');
1078:
1079: if (_isContentType('application/json')) {
1080: _jsonError($message, '', 403);
1081: }
1082:
1083: _header(403);
1084:
1085: _cfg('layoutMode', true);
1086: include(INC . 'tpl/403.php');
1087: exit;
1088: }
1089:
1090: /**
1091: * Display 404 page
1092: * @param string $message The error message
1093: * @param string $entity The entity name
1094: * @return void
1095: */
1096: function _page404($message = '', $entity = '')
1097: {
1098: $message = $message ?: _t('404 Not Found');
1099:
1100: if (_isContentType('application/json')) {
1101: _jsonError($message, $entity, 404);
1102: }
1103:
1104: _header(404);
1105:
1106: _cfg('layoutMode', true);
1107: include(INC . 'tpl/404.php');
1108: exit;
1109: }
1110:
1111: /**
1112: * Display error page
1113: * @param string $message The error message
1114: * @param int $code The error code
1115: * @param int $status HTTP status code
1116: * @return void
1117: */
1118: function _error($message, $code, $status = 500)
1119: {
1120: if (_isContentType('application/json')) {
1121: _json(['error' => $message], $status);
1122: }
1123:
1124: _cfg('layoutMode', true);
1125: _g('httpStatusCode', $status);
1126: $type = __kernelErrorTypes($code);
1127:
1128: _header($status);
1129:
1130: include(INC . 'tpl/exception.php');
1131: exit;
1132: }
1133:
1134: /**
1135: * Check if the current routing is a particular URL RewriteRule processing or not
1136: * @return boolean
1137: */
1138: function _isRewriteRule()
1139: {
1140: return strcasecmp(REQUEST_URI, _r()) !== 0;
1141: }
1142:
1143: /**
1144: * Setter for canonical URL if the argument is given and print the canonical link tag if the argument is not given
1145: * @param string $url The specific URL
1146: * @return void|string
1147: */
1148: function _canonical($url = null)
1149: {
1150: global $lc_canonical;
1151: if (!is_null($url)) {
1152: $lc_canonical = $url;
1153: } else {
1154: return (_cfg('canonical')) ? _cfg('canonical') : _url();
1155: }
1156: }
1157:
1158: /**
1159: * Print hreflang for language and regional URLs
1160: * @return void
1161: */
1162: function _hreflang()
1163: {
1164: global $lc_languages;
1165: if (_multilingual()) {
1166: foreach ($lc_languages as $hrefLang => $langDesc) {
1167: if (_canonical() == _url()) {
1168: $alternate = _url('', null, $hrefLang);
1169: $xdefault = _url('', null, false);
1170: } else {
1171: $alternate = preg_replace('/\/'._lang().'\b/', '/'.$hrefLang, _canonical());
1172: $xdefault = preg_replace('/\/'._lang().'\b/', '', _canonical());
1173: }
1174: echo '<link rel="alternate" hreflang="'.$hrefLang.'" href="'.$alternate.'" />'."\n";
1175: }
1176: echo '<link rel="alternate" hreflang="x-default" href="'.$xdefault.'" />'."\n";
1177: }
1178: }
1179:
1180: /**
1181: * Check if the URI has a language code and return it when it matches
1182: * For example,
1183: *
1184: * - /LucidFrame/en/....
1185: * - /LucidFrame/....
1186: * - /en/...
1187: * - /....
1188: *
1189: * @return mixed The language code if it has one, otherwise return FALSE
1190: */
1191: function _getLangInURI()
1192: {
1193: global $lc_languages;
1194:
1195: if (!isset($_SERVER['REQUEST_URI'])) {
1196: return false;
1197: }
1198:
1199: if (!is_array($lc_languages)) {
1200: $lc_languages = array('en' => 'English');
1201: }
1202:
1203: $baseURL = trim(_cfg('baseURL'), '/');
1204: $baseURL = ($baseURL) ? "/$baseURL/" : '/';
1205: $baseURL = str_replace('/', '\/', $baseURL); // escape literal `/`
1206: $baseURL = str_replace('.', '\.', $baseURL); // escape literal `.`
1207: $regex = '/^('.$baseURL.')\b('.implode('|', array_keys($lc_languages)).'){1}\b(\/?)/i';
1208:
1209: if (preg_match($regex, $_SERVER['REQUEST_URI'], $matches)) {
1210: return $matches[2];
1211: }
1212:
1213: return false;
1214: }
1215:
1216: /**
1217: * Validate that a hostname (for example $_SERVER['HTTP_HOST']) is safe.
1218: *
1219: * @param string $host The host name
1220: * @return boolean TRUE if only containing valid characters, or FALSE otherwise.
1221: */
1222: function _validHost($host)
1223: {
1224: return preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host);
1225: }
1226:
1227: /**
1228: * Get the page title glued by a separator
1229: *
1230: * @param mixed $args multiple arguments or array of arguments
1231: * @return string The formatted page title
1232: */
1233: function _title()
1234: {
1235: global $lc_siteName;
1236: global $lc_titleSeparator;
1237:
1238: $args = func_get_args();
1239:
1240: if (count($args) == 0) {
1241: $title = _app('title');
1242: if (is_array($title)) {
1243: $args = $title;
1244: }
1245:
1246: if (is_string($title)) {
1247: $args = array($title);
1248: }
1249: }
1250:
1251: if (count($args) == 0) {
1252: return $lc_siteName;
1253: }
1254:
1255: if (count($args) == 1) {
1256: if (is_array($args[0])) {
1257: $args = _filterArrayEmpty($args[0]);
1258: $title = $args;
1259: } else {
1260: $title = ($args[0]) ? array($args[0]) : array();
1261: }
1262: } else {
1263: $args = _filterArrayEmpty($args);
1264: $title = $args;
1265: }
1266:
1267: $lc_titleSeparator = trim($lc_titleSeparator);
1268: if ($lc_titleSeparator) {
1269: $lc_titleSeparator = ' '.$lc_titleSeparator.' ';
1270: } else {
1271: $lc_titleSeparator = ' ';
1272: }
1273:
1274: if (count($title)) {
1275: $title = implode($lc_titleSeparator, $title);
1276: if ($lc_siteName) {
1277: $title .= ' | '.$lc_siteName;
1278: }
1279:
1280: return strip_tags($title);
1281: }
1282:
1283: return $lc_siteName;
1284: }
1285:
1286: /**
1287: * Filters elements of an array which have empty values
1288: *
1289: * @param array $input The input array
1290: * @return array The filtered array
1291: */
1292: function _filterArrayEmpty($input)
1293: {
1294: return array_filter($input, '_notEmpty');
1295: }
1296:
1297: /**
1298: * Check the given value is not empty
1299: *
1300: * @param string $value The value to be checked
1301: * @return boolean TRUE if not empty; FALSE if empty
1302: */
1303: function _notEmpty($value)
1304: {
1305: return trim($value) !== '';
1306: }
1307:
1308: /**
1309: * Generate breadcrumb by a separator
1310: *
1311: * @param mixed $args Array of string arguments or multiple string arguments
1312: * @return void
1313: */
1314: function _breadcrumb()
1315: {
1316: global $lc_breadcrumbSeparator;
1317:
1318: $args = func_get_args();
1319:
1320: if (!$lc_breadcrumbSeparator) {
1321: $lc_breadcrumbSeparator = '&raquo;';
1322: }
1323:
1324: if (count($args) == 1 && is_array($args[0])) {
1325: $args = $args[0];
1326: }
1327:
1328: echo implode(" {$lc_breadcrumbSeparator} ", $args);
1329: }
1330:
1331: /**
1332: * Shorten a string for the given length
1333: *
1334: * @param string $str A plain text string to be shortened
1335: * @param integer $length The character count
1336: * @param string $trail To append `...` or not. `null` to not show
1337: *
1338: * @return string The shorten text string
1339: */
1340: function _shorten($str, $length = 50, $trail = '...')
1341: {
1342: if (empty($str)) {
1343: return $str;
1344: }
1345:
1346: $str = strip_tags(trim($str));
1347: if (strlen($str) <= $length) {
1348: return $str;
1349: }
1350:
1351: $short = trim(substr($str, 0, $length));
1352: $lastSpacePos = strrpos($short, ' ');
1353: if ($lastSpacePos !== false) {
1354: $short = substr($short, 0, $lastSpacePos);
1355: }
1356:
1357: if ($trail) {
1358: $short = rtrim($short, '.') . $trail;
1359: }
1360:
1361: return $short;
1362: }
1363:
1364: if (!function_exists('_fstr')) {
1365: /**
1366: * Format a string
1367: *
1368: * @param string|array $value A text string or array of text strings to be formatted
1369: * @param string $glue The glue string between each element
1370: * @param string $lastGlue The glue string between the last two elements
1371: *
1372: * @return string The formatted text string
1373: */
1374: function _fstr($value, $glue = ', ', $lastGlue = 'and')
1375: {
1376: if (empty($value)) {
1377: return $value;
1378: }
1379:
1380: if (!is_array($value)) {
1381: return $value == '' ? _nullFill($value) : nl2br($value);
1382: } elseif (is_array($value) && sizeof($value) > 1) {
1383: $last = array_slice($value, -2, 2);
1384: $lastImplode = implode(' '.$lastGlue.' ', $last);
1385: $first = array_slice($value, 0, sizeof($value)-2);
1386: $firstImplode = implode($glue, $first);
1387:
1388: return $firstImplode ? $firstImplode.$glue.$lastImplode : $lastImplode;
1389: } else {
1390: return nl2br($value[0]);
1391: }
1392: }
1393: }
1394:
1395: if (!function_exists('_fnum')) {
1396: /**
1397: * Format a number
1398: *
1399: * @param int $value A number to be formatted
1400: * @param int $decimals The decimal places. Default is 2.
1401: * @param string $unit The unit appended to the number (optional)
1402: *
1403: * @return string The formatted number
1404: */
1405: function _fnum($value, $decimals = 2, $unit = '')
1406: {
1407: if ($value === '') {
1408: return _nullFill($value);
1409: } elseif (is_numeric($value)) {
1410: $value = number_format($value, $decimals, '.', ',');
1411:
1412: return $unit ? $value . ' ' . $unit : $value;
1413: }
1414:
1415: return $value;
1416: }
1417: }
1418:
1419: if (!function_exists('_fnumSmart')) {
1420: /**
1421: * Format a number in a smarter way, i.e., decimal places are omitted when necessary.
1422: * Given the 2 decimal places, the value 5.00 will be shown 5 whereas the value 5.01 will be shown as it is.
1423: *
1424: * @param int $value A number to be formatted
1425: * @param int $decimals The decimal places. Default is 2.
1426: * @param string $unit The unit appended to the number (optional)
1427: *
1428: * @return string The formatted number
1429: */
1430: function _fnumSmart($value, $decimals = 2, $unit = '')
1431: {
1432: $value = _fnum($value, $decimals, $unit);
1433: $v = explode('.', $value);
1434: if ($decimals > 0 && isset($v[1])) {
1435: if (preg_match('/0{'.$decimals.'}/i', $v[1])) {
1436: $value = $v[0];
1437: }
1438: }
1439:
1440: return $value;
1441: }
1442: }
1443:
1444: if (!function_exists('_fnumReverse')) {
1445: /**
1446: * Remove the number formatting (e.g., thousand separator) from the given number
1447: *
1448: * @param mixed $num A number to remove the formatting
1449: * @return mixed The number
1450: */
1451: function _fnumReverse($num)
1452: {
1453: return str_replace(',', '', $num);
1454: }
1455: }
1456:
1457: if (!function_exists('_fdate')) {
1458: /**
1459: * Format a date
1460: *
1461: * @param string|int $date A date to be formatted
1462: * @param string $format The date format; The config variable will be used if it is not passed
1463: * @return string The formatted date
1464: */
1465: function _fdate($date = '', $format = '')
1466: {
1467: if (!$format) {
1468: $format = _cfg('dateFormat');
1469: }
1470:
1471: if (func_num_args() === 0) {
1472: return date($format);
1473: }
1474:
1475: if (empty($date)) {
1476: return '';
1477: }
1478:
1479: return is_string($date) ? date($format, strtotime($date)) : date($format, $date);
1480: }
1481: }
1482:
1483: if (!function_exists('_fdatetime')) {
1484: /**
1485: * Format a date/time
1486: *
1487: * @param string|int $dateTime A date/time to be formatted
1488: * @param string $format The date/time format; The config variable will be used if it is not passed
1489: * @return string The formatted date/time
1490: */
1491: function _fdatetime($dateTime = '', $format = '')
1492: {
1493: if (!$format) {
1494: $format = _cfg('dateTimeFormat');
1495: }
1496:
1497: if (func_num_args() == 0) {
1498: return date($format);
1499: }
1500:
1501: if (empty($dateTime)) {
1502: return '';
1503: }
1504:
1505: return is_string($dateTime) ? date($format, strtotime($dateTime)) : date($format, $dateTime);
1506: }
1507: }
1508:
1509: if (!function_exists('_ftimeAgo')) {
1510: /**
1511: * Display elapsed time in wording, e.g., 2 hours ago, 1 year ago, etc.
1512: *
1513: * @param string|int $time The elapsed time in unix timestamp or date/time string
1514: * @param string $format The date/time format to show when 4 days passed
1515: * @return string
1516: */
1517: function _ftimeAgo($time, $format = 'M j Y')
1518: {
1519: if (empty($time)) {
1520: return '';
1521: }
1522:
1523: $now = time();
1524: if (!is_numeric($time)) {
1525: $time = strtotime($time);
1526: }
1527:
1528: $secElapsed = $now - $time;
1529: if ($secElapsed <= 60) {
1530: return _t('just now');
1531: } elseif ($secElapsed <= 3540) {
1532: $min = $now - $time;
1533: $min = round($min/60);
1534: return _t('%d minutes ago', $min);
1535: } elseif ($secElapsed <= 3660) {
1536: return _t('1 hour ago');
1537: } elseif (date('j-n-y', $now) == date('j-n-y', $time)) {
1538: return date("g:i a", $time);
1539: } elseif (date('j-n-y', mktime(0, 0, 0, date('n', $now), date('j', $now)-1, date('Y', $now))) == date('j-n-y', $time)) {
1540: return _t('yesterday');
1541: } elseif ($secElapsed <= 345600) {
1542: return date('l', $time);
1543: } else {
1544: return date($format, $time);
1545: }
1546: }
1547: }
1548:
1549: if (!function_exists('_msg')) {
1550: /**
1551: * Print or return the message formatted with HTML
1552: *
1553: * @param mixed $msg A message string or Array of message strings
1554: * @param string $class The CSS class name
1555: * @param mixed $return What is expected to return from this function.
1556: * `null` (default) no return and just print it.
1557: * `html` return HTML.
1558: * @param string $display CSs display property value - block, none, inline-block, etc.
1559: *
1560: * @return string The formatted date
1561: */
1562: function _msg($msg, $class = 'error', $return = null, $display = null)
1563: {
1564: $class = $class ?: 'error';
1565: $return = strtolower($return);
1566:
1567: $html = '';
1568: $html .= '<div class="message"';
1569: if ($display) {
1570: $html .= ' style="display:' . $display . '"';
1571: }
1572: $html .= '>';
1573: $html .= '<div class="message-'. $class . ' alert alert-' . ($class == 'error' ? 'danger' : $class) . '">';
1574: if (is_array($msg)) {
1575: if (count($msg) > 0) {
1576: $html .= '<ul>';
1577: foreach ($msg as $m) {
1578: if (is_array($msg) && isset($m['message'])) {
1579: $html .= '<li>'.$m['message'].'</li>';
1580: } else {
1581: $html .= '<li>'.$m.'</li>';
1582: }
1583: }
1584: $html .= '</ul>';
1585: } else {
1586: $html = '';
1587: }
1588: } else {
1589: $html .= $msg;
1590: }
1591:
1592: $html .= '</div></div>';
1593:
1594: if (is_array($msg) && count($msg) == 0) {
1595: $html = '';
1596: }
1597:
1598: if ($return == 'html' || $return === true) {
1599: return $html;
1600: } else {
1601: echo $html;
1602: }
1603:
1604: return '';
1605: }
1606: }
1607:
1608: /**
1609: * Find the size of the given file.
1610: *
1611: * @param string $file The file name (file must exist)
1612: * @param int $digits Number of precisions
1613: * @param array $sizes Array of size units, e.g., array("TB","GB","MB","KB","B"). Default is array("MB","KB","B")
1614: *
1615: * @return string|bool The size unit (B, KiB, MiB, GiB, TiB, PiB, EiB, ZiB, YiB) or `FALSE` for non-existence file
1616: */
1617: function _filesize($file, $digits = 2, $sizes = array("MB","KB","B"))
1618: {
1619: if (is_file($file)) {
1620: $filePath = $file;
1621: if (!realpath($filePath)) {
1622: $filePath = $_SERVER["DOCUMENT_ROOT"].$filePath;
1623: }
1624: $fileSize = filesize($filePath);
1625: $total = count($sizes);
1626: while ($total-- && $fileSize > 1024) {
1627: $fileSize /= 1024;
1628: }
1629:
1630: return round($fileSize, $digits)." ".$sizes[$total];
1631: }
1632:
1633: return false;
1634: }
1635:
1636: if (!function_exists('_randomCode')) {
1637: /**
1638: * Generate a random string from the given array of letters.
1639: * @param int $length The length of required random string
1640: * @param array $letters Array of letters from which randomized string is derived from.
1641: * Default is a to z and 0 to 9.
1642: * @param string $prefix Prefix to the generated string
1643: * @return string The random string of required length
1644: */
1645: function _randomCode($length = 5, $letters = array(), $prefix = '')
1646: {
1647: # Letters & Numbers for default
1648: if (sizeof($letters) == 0) {
1649: $letters = array_merge(range(0, 9), range('a', 'z'), range('A', 'Z'));
1650: }
1651:
1652: shuffle($letters); # Shuffle letters
1653: $randArr = array_splice($letters, 0, $length);
1654:
1655: return $prefix . implode('', $randArr);
1656: }
1657: }
1658:
1659: if (!function_exists('_slug')) {
1660: /**
1661: * Generate a slug of human-readable keywords
1662: *
1663: * @param string $string Text to slug
1664: * @param string $table Table name to check in. If it is empty, no check in the table
1665: * @param array $condition Condition to append table check-in, e.g, `array('fieldName !=' => value)`
1666: *
1667: * @return string The generated slug
1668: */
1669: function _slug($string, $table = '', array $condition = array())
1670: {
1671: if (empty($string)) {
1672: return '';
1673: }
1674:
1675: $specChars = array(
1676: '`','~','!','@','#','$','%','\^','&',
1677: '*','(',')','=','+','{','}','[',']',
1678: ':',';',"'",'"','<','>','\\','|','?','/',','
1679: );
1680: $table = db_table($table);
1681: $slug = strtolower(trim($string));
1682: $slug = trim($slug, '-');
1683: # clear special characters
1684: $slug = preg_replace('/(&amp;|&quot;|&#039;|&lt;|&gt;)/i', '', $slug);
1685: $slug = str_replace($specChars, '-', $slug);
1686: $slug = str_replace(array(' ', '.'), '-', $slug);
1687: $slug = trim($slug, '-');
1688:
1689: $condition = array_merge(
1690: array('slug' => $slug),
1691: $condition
1692: );
1693:
1694: while (true && $table) {
1695: $count = db_count($table)->where($condition)->fetch();
1696: if ($count == 0) {
1697: break;
1698: }
1699:
1700: $segments = explode('-', $slug);
1701: if (sizeof($segments) > 1 && is_numeric($segments[sizeof($segments)-1])) {
1702: $index = array_pop($segments);
1703: $index++;
1704: } else {
1705: $index = 1;
1706: }
1707:
1708: $segments[] = $index;
1709: $slug = implode('-', $segments);
1710: }
1711:
1712: $slug = preg_replace('/[\-]+/', '-', $slug);
1713:
1714: return trim($slug, '-');
1715: }
1716: }
1717:
1718: /**
1719: * Return the SQL date (Y-m-d) from the given date and format
1720: *
1721: * @param string $date Date to convert
1722: * @param string $givenFormat Format for the given date
1723: * @param string $separator Separator in the date. Default is dash "-"
1724: *
1725: * @return string|null the SQL date string if the given date is valid, otherwise null
1726: */
1727: function _sqlDate($date, $givenFormat = 'dmy', $separator = '-')
1728: {
1729: if (empty($date)) {
1730: return null;
1731: }
1732:
1733: $dt = explode($separator, $date);
1734: $format = str_split($givenFormat);
1735: $ft = array_flip($format);
1736:
1737: $y = $dt[$ft['y']];
1738: $m = $dt[$ft['m']];
1739: $d = $dt[$ft['d']];
1740:
1741: return checkdate($m, $d, $y) ? $y . '-' . $m .'-'. $d : null;
1742: }
1743:
1744: /**
1745: * Encrypts the given text using security salt
1746: * IMPORTANT: This function will only work if openssl extension is enabled
1747: * and the .secret file exists after running `php lucidframe secret:generate`
1748: *
1749: * @param string $text Text to be encrypted
1750: * @return string The encrypted text
1751: */
1752: function _encrypt($text)
1753: {
1754: $secret = _cfg('securitySecret');
1755: if (!$secret || !function_exists('openssl_encrypt')) {
1756: return $text;
1757: }
1758:
1759: $method = _cipher();
1760: $ivlen = openssl_cipher_iv_length($method);
1761: $iv = openssl_random_pseudo_bytes($ivlen);
1762:
1763: $textRaw = openssl_encrypt($text, $method, $secret, OPENSSL_RAW_DATA, $iv);
1764: $hmac = hash_hmac('sha256', $textRaw, $secret, true);
1765:
1766: return base64_encode($iv . $hmac . $textRaw );
1767: }
1768:
1769: /**
1770: * Decrypts the given text using security salt
1771: * IMPORTANT: This function will only work if openssl extension is enabled
1772: * and the .secret file exists after running `php lucidframe secret:generate`
1773: *
1774: * @param string $encryptedText Text to be decrypted
1775: * @return string The decrypted text
1776: */
1777: function _decrypt($encryptedText)
1778: {
1779: $secret = _cfg('securitySecret');
1780: if (!$secret || !function_exists('openssl_decrypt')) {
1781: return $encryptedText;
1782: }
1783:
1784: $method = _cipher();
1785: $sha2len = 32;
1786: $ivlen = openssl_cipher_iv_length($method);
1787: $text = base64_decode($encryptedText);
1788: $iv = substr($text, 0, $ivlen);
1789:
1790: $rawText = substr($text, $ivlen + $sha2len);
1791: $plainText = openssl_decrypt($rawText, $method, $secret, OPENSSL_RAW_DATA, $iv);
1792:
1793: $hmac = substr($text, $ivlen, $sha2len);
1794: $mac = hash_hmac('sha256', $rawText, $secret, true);
1795: if (hash_equals($hmac, $mac)) {
1796: return $plainText;
1797: }
1798:
1799: return $text;
1800: }
1801:
1802: /**
1803: * Get current cipher method
1804: * @return string
1805: */
1806: function _cipher()
1807: {
1808: $method = _cfg('cipher');
1809: if (!in_array($method, openssl_get_cipher_methods())) {
1810: $method = 'AES-256-CBC';
1811: }
1812:
1813: return $method;
1814: }
1815:
1816: /**
1817: * Simple quick helper function for <meta> tag attribute values
1818: *
1819: * @param string $key The <meta> tag name
1820: * @param string $value If the value is empty, this is a Getter function; otherwise Setter function
1821: * @return void|mixed
1822: */
1823: function _meta($key, $value = '')
1824: {
1825: global $_meta;
1826: $value = trim($value);
1827: if (empty($value)) {
1828: return (isset($_meta[$key])) ? $_meta[$key] : '';
1829: } else {
1830: if (in_array($key, array('description', 'og:description', 'twitter:description', 'gp:description'))) {
1831: $value = trim(substr($value, 0, 200));
1832: }
1833: $_meta[$key] = $value;
1834: }
1835: }
1836:
1837: /**
1838: * Print SEO meta tags
1839: * @return void
1840: */
1841: function _metaSeoTags()
1842: {
1843: if (_meta('description')) {
1844: _cfg('metaDescription', _meta('description'));
1845: }
1846:
1847: $tags = array();
1848: $tags['description'] = _cfg('metaDescription');
1849:
1850: $tags['og'] = array();
1851: $tags['og']['title'] = _meta('og:title') ? _meta('og:title') : _title();
1852: $tags['og']['url'] = _meta('og:url') ? _meta('og:url') : _url();
1853: $tags['og']['type'] = _meta('og:type') ? _meta('og:type') : 'website';
1854: $tags['og']['image'] = _meta('og:image') ? _meta('og:image') : _img('logo-social.jpg');
1855: $tags['og']['description'] = _meta('og:description') ? _meta('og:description') : _cfg('metaDescription');
1856: $tags['og']['site_name'] = _meta('og:site_name') ? _meta('og:site_name') : _cfg('siteName');
1857:
1858: $tags['twitter'] = array();
1859: $tags['twitter']['card'] = _meta('twitter:card') ? _meta('twitter:card') : 'summary';
1860: $tags['twitter']['site'] = _meta('twitter:site') ? '@'._meta('twitter:site') : '@'._cfg('siteDomain');
1861: $tags['twitter']['title'] = _meta('twitter:title') ? _meta('twitter:title') : _title();
1862: $tags['twitter']['description'] = _meta('twitter:description') ? _meta('twitter:description') : _cfg('metaDescription');
1863: $tags['twitter']['image'] = _meta('twitter:image') ? _meta('twitter:image') : _img('logo-social.jpg');
1864:
1865: if (function_exists('__metaSeoTags')) {
1866: echo __metaSeoTags($tags);
1867: } else {
1868: echo "\n";
1869: foreach ($tags as $name => $tag) {
1870: if ($name == 'og') {
1871: foreach ($tag as $key => $content) {
1872: echo '<meta property="og:' . $key . '" content="' . $content . '" />'."\n";
1873: }
1874: } elseif ($name == 'twitter') {
1875: foreach ($tag as $key => $content) {
1876: echo '<meta name="twitter:' . $key . '" content="' . $content . '" />'."\n";
1877: }
1878: } else {
1879: echo '<meta name="' . $name . '" content="' . $tag . '" />'."\n";
1880: }
1881: }
1882: }
1883: }
1884:
1885: /**
1886: * Simple mail helper function
1887: * The formatting of the email addresses must comply with RFC 2822. Some examples are:
1888: *
1889: * - user@example.com
1890: * - user@example.com, anotheruser@example.com
1891: * - User <user@example.com>
1892: * - User <user@example.com>, Another User <anotheruser@example.com>*
1893: *
1894: * @param string $from The sender of the mail
1895: * @param string $to The receiver or receivers of the mail
1896: * @param string $subject Subject of the email to be sent.
1897: * @param string $message Message to be sent
1898: * @param string $cc The CC receiver or receivers of the mail
1899: * @param string $bcc The Bcc receiver or receivers of the mail
1900: *
1901: * @return boolean Returns TRUE if the mail was successfully accepted for delivery, FALSE otherwise
1902: */
1903: function _mail($from, $to, $subject = '', $message = '', $cc = '', $bcc = '')
1904: {
1905: $charset = mb_detect_encoding($message);
1906: $message = nl2br(stripslashes($message));
1907:
1908: $EEOL = PHP_EOL; //"\n";
1909: $headers = 'From: ' . $from . $EEOL;
1910: $headers .= 'MIME-Version: 1.0' . $EEOL;
1911: $headers .= 'Content-type: text/html; charset=' . $charset . $EEOL;
1912: $headers .= 'Reply-To: ' . $from . $EEOL;
1913: $headers .= 'Return-Path:'.$from . $EEOL;
1914: if ($cc) {
1915: $headers .= 'Cc: ' . $cc . $EEOL;
1916: }
1917: if ($bcc) {
1918: $headers .= 'Bcc: ' . $bcc . $EEOL;
1919: }
1920: $headers .= 'X-Mailer: PHP';
1921:
1922: return mail($to, $subject, $message, $headers);
1923: }
1924: /**
1925: * Get translation strings from the POST array
1926: * and prepare to insert or update into the table according to the specified fields
1927: *
1928: * @param array $post The POST array
1929: * @param array $fields The array of field name and input name mapping, e.g., array('fieldName' => 'inputName')
1930: * @param string $lang The language code to fetch (if it is not provided, all languages will be fetched)
1931: *
1932: * @return array The data array
1933: */
1934: function _postTranslationStrings($post, $fields, $lang = null)
1935: {
1936: global $lc_languages;
1937:
1938: $data = array();
1939: foreach ($fields as $key => $name) {
1940: if ($lang) {
1941: $lcode = _queryLang($lang);
1942: if (isset($post[$name.'_'.$lcode])) {
1943: $data[$key.'_'.$lcode] = $post[$name.'_'.$lcode];
1944: }
1945: } else {
1946: if (isset($post[$name])) {
1947: $data[$key.'_'._defaultLang()] = $post[$name];
1948: }
1949: foreach ($lc_languages as $lcode => $lname) {
1950: $lcode = _queryLang($lcode);
1951: if (isset($post[$name.'_'.$lcode])) {
1952: $data[$key.'_'.$lcode] = $post[$name.'_'.$lcode];
1953: }
1954: }
1955: }
1956: }
1957:
1958: return $data;
1959: }
1960:
1961: /**
1962: * Get translation strings from the query result
1963: * and return the array of `$i18n[fieldName][lang] = $value`
1964: *
1965: * @param object|array $data The query result
1966: * @param array|string $fields The array of field names to get data, e.g.,
1967: * 'fieldName' or `array('fieldName1', 'fieldName2')`
1968: * @param string $lang The language code to fetch (if it is not provided, all languages will be fetched)
1969: *
1970: * @return array|object The array or object of translation strings
1971: */
1972: function _getTranslationStrings($data, $fields, $lang = null)
1973: {
1974: global $lc_languages;
1975:
1976: $isObject = is_object($data);
1977: $data = (array) $data;
1978:
1979: if (is_string($fields)) {
1980: $fields = array($fields);
1981: }
1982:
1983: foreach ($fields as $name) {
1984: if ($lang) {
1985: $lcode = _queryLang($lang);
1986: if (isset($data[$name.'_'.$lcode]) && $data[$name.'_'.$lcode]) {
1987: $data[$name.'_i18n'] = $data[$name.'_'.$lcode];
1988: } else {
1989: $data[$name.'_i18n'] = $data[$name];
1990: }
1991: } else {
1992: foreach ($lc_languages as $lcode => $lname) {
1993: $lcode = _queryLang($lcode);
1994: if (isset($data[$name.'_'.$lcode])) {
1995: $data[$name.'_i18n'][$lcode] = $data[$name.'_'.$lcode];
1996: }
1997: }
1998: }
1999: }
2000:
2001: if ($isObject) {
2002: $data = (object) $data;
2003: }
2004:
2005: return $data;
2006: }
2007:
2008: /**
2009: * Detect the current page visited by a search bot or crawler
2010: * @return boolean `TRUE` if it is a bot's visit; otherwise `FALSE`
2011: * @see http://www.useragentstring.com/pages/useragentstring.php?typ=Crawler
2012: */
2013: function _isBot()
2014: {
2015: if (!isset($_SERVER['HTTP_USER_AGENT'])) {
2016: return false;
2017: }
2018:
2019: $userAgent = $_SERVER['HTTP_USER_AGENT'];
2020: if (empty($userAgent)) {
2021: return false;
2022: }
2023:
2024: $bots = array(
2025: 'Googlebot',
2026: 'Slurp',
2027: 'msnbot',
2028: 'bingbot',
2029: 'yahoo',
2030: 'search.msn.com',
2031: 'Baidu',
2032: 'baiduspider',
2033: 'Yandex',
2034: 'nutch',
2035: 'FAST',
2036: 'Sosospider',
2037: 'Exabot',
2038: 'sogou',
2039: 'bot',
2040: 'crawler',
2041: 'spider',
2042: 'Feedfetcher-Google',
2043: 'ASPSeek',
2044: 'simpy',
2045: 'ips-agent',
2046: 'Libwww-perl',
2047: 'ask jeeves',
2048: 'fastcrawler',
2049: 'infoseek',
2050: 'lycos',
2051: 'mediapartners-google',
2052: 'CRAZYWEBCRAWLER',
2053: 'adsbot-google',
2054: 'curious george',
2055: 'ia_archiver',
2056: 'MJ12bot',
2057: 'Uptimebot',
2058: 'Dataprovider.com',
2059: 'Go-http-client',
2060: 'Barkrowler',
2061: 'panscient.com',
2062: 'Symfony BrowserKit',
2063: 'Apache-HttpClient',
2064: 'serpstatbot',
2065: 'BLEXBot',
2066: 'DotBot',
2067: 'AhrefsBot',
2068: );
2069: foreach ($bots as $bot) {
2070: if (false !== strpos(strtolower($userAgent), strtolower($bot))) {
2071: return true;
2072: }
2073: }
2074:
2075: return false;
2076: }
2077:
2078: /**
2079: * Write output
2080: * @since PHPLucidFrame v 1.14.0
2081: * @param string $text The text to output
2082: * @param [mixed $args [, mixed ...]] Arguments to the text
2083: * @return void
2084: */
2085: function _write($text = '')
2086: {
2087: $args = func_get_args();
2088: $text = array_shift($args);
2089: if ($text) {
2090: echo vsprintf($text, $args);
2091: }
2092: }
2093:
2094: /**
2095: * Write output with line feed (\n)
2096: * @since PHPLucidFrame v 1.11.0
2097: * @param string $text The text to output
2098: * @param [mixed $args [, mixed ...]] Arguments to the text
2099: * @return void
2100: */
2101: function _writeln($text = '')
2102: {
2103: $args = func_get_args();
2104: $text = array_shift($args);
2105: if ($text) {
2106: echo vsprintf($text, $args);
2107: }
2108:
2109: echo "\n";
2110: }
2111:
2112: /**
2113: * Write spacer for indentation purpose
2114: * @since PHPLucidFrame v 1.11.0
2115: * @param int $width No. of spaces
2116: * @return void|string
2117: */
2118: function _indent($width = 2)
2119: {
2120: return str_repeat(' ', $width);
2121: }
2122:
2123: /**
2124: * Simple helper to create an instance of LucidFrame\Console\Command
2125: * @since PHPLucidFrame v 1.11.0
2126: * @param string $command The command name
2127: * @return object LucidFrame\Console\Command
2128: */
2129: function _consoleCommand($command)
2130: {
2131: return new Command($command);
2132: }
2133:
2134: /**
2135: * Simple helper to create an instance of LucidFrame\Console\ConsoleTable
2136: * @since PHPLucidFrame v 1.12.0
2137: * @return object LucidFrame\Console\ConsoleTable
2138: */
2139: function _consoleTable()
2140: {
2141: return new ConsoleTable();
2142: }
2143:
2144: /**
2145: * Simple helper to get all registered commands
2146: * @since PHPLucidFrame v 1.12.0
2147: * @return array The array of command LucidFrame\Console\Command
2148: */
2149: function _consoleCommands()
2150: {
2151: return Console::getCommands();
2152: }
2153:
2154: /**
2155: * Simple helper to create Pager object
2156: * @since PHPLucidFrame v 1.11.0
2157: * @param string $pageQueryStr The customized page query string name, default is page
2158: * @return object LucidFrame\Core\Pager
2159: */
2160: function _pager($pageQueryStr = '')
2161: {
2162: return new Pager($pageQueryStr);
2163: }
2164:
2165: /**
2166: * Simple helper to create File object
2167: * @since PHPLucidFrame v 1.11.0
2168: * @param string $fileName (optional) Path to the file
2169: * @return object LucidFrame\File\File
2170: */
2171: function _fileHelper($fileName = '')
2172: {
2173: return new File($fileName);
2174: }
2175:
2176: /**
2177: * Simple helper to create AsynFileUploader object
2178: * @since PHPLucidFrame v 1.11.0
2179: * @param string/array anonymous The input file name or The array of property/value pairs
2180: * @return object LucidFrame\File\AsynFileUploader
2181: */
2182: function _asynFileUploader()
2183: {
2184: if (func_num_args()) {
2185: return new AsynFileUploader(func_get_arg(0));
2186: } else {
2187: return new AsynFileUploader();
2188: }
2189: }
2190:
2191: /**
2192: * Simple helper to register a middleware
2193: * @since PHPLucidFrame v 2.0.0
2194: * @param Closure $closure Anonymous function
2195: * @param string $event before (default) or after
2196: * @return object LucidFrame\Core\Middleware
2197: */
2198: function _middleware(\Closure $closure, $event = 'before')
2199: {
2200: $middleware = Middleware::getInstance();
2201:
2202: return $middleware->register($closure, $event);
2203: }
2204:
2205: /**
2206: * Get view file
2207: * @return string The view file with absolute path
2208: */
2209: function _view()
2210: {
2211: if (_cfg('view')) {
2212: $viewName = 'view_'._cfg('view');
2213: } elseif (_g('view')) {
2214: $viewName = 'view_'._g('view');
2215: } else {
2216: $viewName = 'view';
2217: }
2218:
2219: return _i(_ds(_cr(), $viewName.'.php'));
2220: }
2221:
2222: /**
2223: * Return directories and file names glued by directory separator
2224: * @return string
2225: */
2226: function _ds()
2227: {
2228: return implode(_DS_, func_get_args());
2229: }
2230:
2231: /**
2232: * Check if the request is an AJAX request
2233: * @return boolean TRUE if the request is XmlHttpRequest, otherwise FALSE
2234: */
2235: function _isAjax()
2236: {
2237: if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && !empty($_SERVER['HTTP_X_REQUESTED_WITH']) &&
2238: strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
2239: return true;
2240: }
2241:
2242: return false;
2243: }
2244:
2245: /**
2246: * Check if HTTP request method is POST and has request data
2247: * @return bool
2248: */
2249: function _isHttpPost()
2250: {
2251: return _isRequestMethod('POST') && count($_POST);
2252: }
2253:
2254: /**
2255: * Header sent as text/json
2256: *
2257: * @param array|object $data Array/Object of data to be encoded as JSON
2258: * @param int $status HTTP status code, default to 200
2259: * @param bool $return Return json data or not
2260: * @return false|string|void
2261: */
2262: function _json($data = [], $status = 200, $return = false)
2263: {
2264: _cfg('layoutMode', false);
2265:
2266: if (_isRequestMethod('OPTIONS')) {
2267: _header(200);
2268: exit;
2269: }
2270:
2271: _header($status);
2272:
2273: header('Content-Type: application/json');
2274: if ($status != 204) {
2275: $json = json_encode($data);
2276: if ($return) {
2277: return $json;
2278: }
2279:
2280: echo $json;
2281: }
2282:
2283: Middleware::runAfter();
2284: exit;
2285: }
2286:
2287: /**
2288: * Response error as JSON
2289: * @param string|array $message The error message or array of error message
2290: * @param string $field The field name
2291: * @param int $status HTTP status code
2292: * @return void
2293: */
2294: function _jsonError($message, $field = '', $status = 400)
2295: {
2296: $errors = [];
2297: if (is_array($message)) {
2298: $errors = $message;
2299: } else {
2300: $errors[] = [
2301: 'field' => $field,
2302: 'message' => $message,
2303: ];
2304:
2305: }
2306:
2307: _json(['error' => $errors], $status);
2308: }
2309:
2310: /**
2311: * Fetch all HTTP request headers
2312: * @return array An associative array of all the HTTP headers in the current request, or FALSE on failure.
2313: */
2314: function _requestHeaders()
2315: {
2316: if (function_exists('getallheaders')) {
2317: return getallheaders();
2318: }
2319:
2320: if (function_exists('apache_request_headers')) {
2321: return apache_request_headers();
2322: }
2323:
2324: $headers = array();
2325: foreach ($_SERVER as $name => $value) {
2326: $name = strtolower($name);
2327: if (substr($name, 0, 5) == 'http_') {
2328: $headers[str_replace(' ', '-', ucwords(str_replace('_', ' ', substr($name, 5))))] = $value;
2329: } elseif ($name == 'content_type') {
2330: $headers['Content-Type'] = $value;
2331: } elseif ($name == 'content_length') {
2332: $headers['Content-Length'] = $value;
2333: }
2334: }
2335:
2336: return $headers;
2337: }
2338:
2339: /**
2340: * Fetch a HTTP request header by name
2341: * @param string $name The HTTP header name
2342: * @return string The HTTP header value from the request
2343: */
2344: function _requestHeader($name)
2345: {
2346: $headers = _requestHeaders();
2347: if (!is_array($headers)) {
2348: return null;
2349: }
2350:
2351: $headers = array_change_key_case($headers);
2352: $name = strtolower($name);
2353:
2354: if (isset($headers[$name])) {
2355: return $headers[$name];
2356: }
2357:
2358: return null;
2359: }
2360:
2361: /**
2362: * Get request method
2363: * @return string|null
2364: */
2365: function _requestMethod()
2366: {
2367: if (isset($_SERVER['REQUEST_METHOD'])) {
2368: return strtoupper($_SERVER['REQUEST_METHOD']);
2369: }
2370:
2371: return null;
2372: }
2373:
2374: /**
2375: * Check if the request method is the given one
2376: * @param string $method The request method
2377: * @return bool
2378: */
2379: function _isRequestMethod($method)
2380: {
2381: return _requestMethod() == strtoupper($method);
2382: }
2383:
2384: /**
2385: * Convert form data into js variable
2386: * @param string $name The form name or scope name
2387: * @param array $data Array of data
2388: */
2389: function _addFormData($name, array $data)
2390: {
2391: echo '<script>LC.Form.formData["' . $name . '"] = ' . json_encode($data) . ';</script>';
2392: }
2393:
2394: /**
2395: * Return a value or empty sign
2396: * Hook to implement `__nullFill()` at app/helpers/utility_helper.php
2397: * @param mixed $value The value to check and show
2398: * @return string
2399: */
2400: function _nullFill($value)
2401: {
2402: if (function_exists('__nullFill')) {
2403: return __nullFill($value);
2404: }
2405:
2406: return $value ?: '<span class="null-fill">-</span>';
2407: }
2408:
2409: /**
2410: * Get default entity object from the schema
2411: * @param string $table The mapped table name without prefix
2412: * @param array $data Array of default data
2413: * @param string|null $dbNamespace The current db namespace
2414: * @return object The empty stdClass object with field names as properties
2415: */
2416: function _entity($table, array $data = [], $dbNamespace = null)
2417: {
2418: if (!$dbNamespace) {
2419: $dbNamespace = _cfg('defaultDbSource');
2420: }
2421:
2422: $schema = _schema($dbNamespace, true);
2423:
2424: $entity = array();
2425: if ($schema && isset($schema[$table])) {
2426: $options = array_merge(SchemaManager::$relationships, array('options'));
2427: foreach ($schema[$table] as $field => $def) {
2428: if (in_array($field, $options)) {
2429: continue;
2430: }
2431:
2432: if (isset($def['autoinc'])) {
2433: $value = 0;
2434: } else {
2435: $value = isset($def['null']) ? null : '';
2436: if (isset($def['default'])) {
2437: $value = $def['default'];
2438: }
2439: }
2440:
2441: if ($field == 'created' || $field == 'updated') {
2442: $value = date('Y-m-i H:i:s');
2443: }
2444:
2445: if ($def['type'] == 'array' || $def['type'] == 'json') {
2446: $value = array();
2447: }
2448:
2449: $entity[$field] = $value;
2450:
2451: if (isset($data[$field])) {
2452: $entity[$field] = $data[$field];
2453: }
2454: }
2455: }
2456:
2457: return (object) $entity;
2458: }
2459:
2460: /**
2461: * Add CSS file to be included in head section
2462: * @param string $file An absolute file path or file name only.
2463: * The file name only will be prepended the folder name css/ and it will be looked in every sub-sites "css" folder
2464: */
2465: function _addHeadStyle($file)
2466: {
2467: $view = _app('view');
2468: $view->addHeadStyle($file);
2469: }
2470:
2471: /**
2472: * Add JS file to be included in head section
2473: * @param string $file An absolute file path or file name only.
2474: * The file name only will be prepended the folder name js/ and it will be looked in every sub-sites "js" folder
2475: */
2476: function _addHeadScript($file)
2477: {
2478: $view = _app('view');
2479: $view->addHeadScript($file);
2480: }
2481:
2482: /**
2483: * Convert English number to Myanmar number
2484: * @param string $num
2485: * @return string
2486: */
2487: function _en2myNum($num)
2488: {
2489: $digits = array(
2490: '/0/' => '၀',
2491: '/1/' => '၁',
2492: '/2/' => '၂',
2493: '/3/' => '၃',
2494: '/4/' => '၄',
2495: '/5/' => '၅',
2496: '/6/' => '၆',
2497: '/7/' => '၇',
2498: '/8/' => '၈',
2499: '/9/' => '၉',
2500: );
2501:
2502: return preg_replace(array_keys($digits), array_values($digits), $num);
2503: }
2504:
2505: /**
2506: * Check if array is associative or sequential
2507: * @param array $arr The array to be checked
2508: * @return bool
2509: */
2510: function _arrayAssoc(array $arr)
2511: {
2512: if (empty($arr)) {
2513: return false;
2514: }
2515:
2516: return array_keys($arr) !== range(0, count($arr) - 1);
2517: }
2518:
2519: /**
2520: * Check if HTTP header has the given content type
2521: * @param string $type HTTP header content type
2522: * @return bool
2523: */
2524: function _isContentType($type)
2525: {
2526: return isset($_SERVER['CONTENT_TYPE']) && $_SERVER['CONTENT_TYPE'] == $type;
2527: }
2528:
2529: /**
2530: * cURL helper
2531: *
2532: * @param string $url The absolute URL
2533: * @param array|string $params The data to send
2534: * @param string $method GET|POST|PUT|DELETE
2535: * @param array $headers The mail headers
2536: * @return array
2537: * - options array The returned data from curl_getinfo
2538: * - error string The returned data from curl_error
2539: * - response mixed The whole response
2540: */
2541: function _curl($url, $params = array(), $method = 'get', $headers = array())
2542: {
2543: $method = strtoupper($method);
2544: if (!in_array($method, array('GET', 'POST', 'PUT', 'DELETE'))) {
2545: $method = 'GET';
2546: }
2547:
2548: $queryStr = '';
2549: if (!empty($params)) {
2550: $queryStr = is_array($params) ? http_build_query($params) : $params;
2551: }
2552:
2553: $ch = curl_init();
2554: if ($method == 'POST') {
2555: curl_setopt($ch, CURLOPT_POST, 1);
2556: if ($queryStr) {
2557: curl_setopt($ch, CURLOPT_POSTFIELDS, $queryStr);
2558: }
2559: } else {
2560: if ($queryStr) {
2561: $url .= '?' . $queryStr;
2562: }
2563: }
2564:
2565: // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
2566: curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
2567: curl_setopt($ch, CURLOPT_URL, $url);
2568: if (!empty($headers)) {
2569: if (_arrayAssoc($headers)) {
2570: $headers = array_map(function($key, $value) {
2571: return $key . ': ' . $value;
2572: }, array_keys($headers), array_values($headers));
2573: }
2574:
2575: curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
2576: }
2577:
2578: $response = curl_exec($ch);
2579: $info = curl_getinfo($ch);
2580: $error = curl_error($ch);
2581:
2582: curl_close($ch);
2583:
2584: return [
2585: 'options' => $info,
2586: 'error' => $error,
2587: 'response' => $response,
2588: ];
2589: }
2590:
2591: /**
2592: * Helper to print select dropdown
2593: * @param string $name The input name
2594: * @param array $data The data for the options to render (key/value pair or array of objects containing id/name properties)
2595: * @param mixed $value The option value to be selected
2596: * @param array $attributes Array of HTML attributes key/value pair
2597: * @return string
2598: */
2599: function form_select($name, $data = [], $value = null, $attributes = [])
2600: {
2601: $attr = '';
2602: foreach ($attributes as $key => $val) {
2603: $attr .= ' ' . $key . '="' . $val . '"';
2604: }
2605:
2606: $html = '<select name="' . $name . '" ' . $attr . '>';
2607: foreach ($data as $key => $val) {
2608: if (isset($val->id) && isset($val->name)) {
2609: $key = $val->id;
2610: $val = $val->name;
2611: }
2612: $html .= '<option value="' . $key . '" ';
2613: $html .= form_selected($name, $key, $value);
2614: $html .= '>' . _t($val) . '</option>';
2615: }
2616: $html .= '</select>';
2617:
2618: return $html;
2619: }
2620:
2621: /**
2622: * Get extension from MIME type
2623: * @param string $mime MIME type
2624: * @return int|string
2625: */
2626: function _mime2ext($mime) {
2627: $mimeMap = [
2628: '3g2' => ['video/3gpp2'],
2629: '3gp' => ['video/3gp', 'video/3gpp'],
2630: '7zip' => ['application/x-compressed'],
2631: 'acc' => ['audio/x-acc'],
2632: 'ac3' => ['audio/ac3'],
2633: 'ai' => ['application/postscript'],
2634: 'aif' => ['audio/x-aiff', 'audio/aiff'],
2635: 'au' => ['audio/x-au'],
2636: 'avi' => ['video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'],
2637: 'bin' => ['application/macbinary', 'application/mac-binary', 'application/x-binary', 'application/mac-binary'],
2638: 'bmp' => ['image/bmp', 'image/x-bmp', 'image/x-bitmap', 'image/x-xbitmap', 'image/x-win-bitmap', 'image/x-windows-bmp', 'image/ms-bmp', 'image/x-ms-bmp', 'application/bmp', 'application/x-bmp', 'application/x-win-bitmap'],
2639: 'cdr' => ['application/cdr', 'application/x-cdr', 'application/coreldraw', 'application/x-coreldraw', 'image/cdr', 'image/x-cdr', 'zz-application/zz-winassoc-cdr'],
2640: 'cpt' => ['application/mac-compactpro'],
2641: 'crl' => ['application/pkix-crl', 'application/pkcs-crl'],
2642: 'crt' => ['application/x-x509-ca-cert', 'application/pkix-cert'],
2643: 'css' => ['text/css'],
2644: 'csv' => ['text/x-comma-separated-values', 'text/comma-separated-values', 'application/vnd.msexcel'],
2645: 'dcr' => ['application/x-director'],
2646: 'doc' => ['application/msword'],
2647: 'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
2648: 'dvi' => ['application/x-dvi'],
2649: 'eml' => ['message/rfc822'],
2650: 'exe' => ['application/x-msdownload'],
2651: 'f4v' => ['video/x-f4v'],
2652: 'flac' => ['audio/x-flac'],
2653: 'flv' => ['video/x-flv'],
2654: 'gif' => ['image/gif'],
2655: 'gpg' => ['application/gpg-keys'],
2656: 'gtar' => ['application/x-gtar'],
2657: 'gzip' => ['application/x-gzip'],
2658: 'hqx' => ['application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'],
2659: 'html' => ['text/html'],
2660: 'ico' => ['image/x-icon', 'image/x-ico', 'image/vnd.microsoft.icon'],
2661: 'ics' => ['text/calendar'],
2662: 'jar' => ['application/java-archive', 'application/x-java-application', 'application/x-jar'],
2663: 'jp2' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'],
2664: 'jpeg' => ['image/jpeg', 'image/pjpeg'],
2665: 'js' => ['application/x-javascript'],
2666: 'json' => ['application/json', 'text/json'],
2667: 'kml' => ['application/vnd.google-earth.kml+xml'],
2668: 'kmz' => ['application/vnd.google-earth.kmz'],
2669: 'log' => ['text/x-log'],
2670: 'm4a' => ['audio/x-m4a', 'audio/mp4'],
2671: 'm4u' => ['application/vnd.mpegurl'],
2672: 'mid' => ['audio/midi'],
2673: 'mif' => ['application/vnd.mif'],
2674: 'mov' => ['video/quicktime'],
2675: 'movie' => ['video/x-sgi-movie'],
2676: 'mp3' => ['audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'],
2677: 'mp4' => ['video/mp4'],
2678: 'mpeg' => ['video/mpeg'],
2679: 'oda' => ['application/oda'],
2680: 'ogg' => ['audio/ogg', 'video/ogg', 'application/ogg'],
2681: 'otf' => ['font/otf'],
2682: 'p10' => ['application/x-pkcs10', 'application/pkcs10'],
2683: 'p12' => ['application/x-pkcs12'],
2684: 'p7a' => ['application/x-pkcs7-signature'],
2685: 'p7c' => ['application/pkcs7-mime', 'application/x-pkcs7-mime'],
2686: 'p7r' => ['application/x-pkcs7-certreqresp'],
2687: 'p7s' => ['application/pkcs7-signature'],
2688: 'pdf' => ['application/pdf', 'application/octet-stream'],
2689: 'pem' => ['application/x-x509-user-cert', 'application/x-pem-file'],
2690: 'pgp' => ['application/pgp'],
2691: 'php' => ['application/x-httpd-php', 'application/php', 'application/x-php', 'text/php', 'text/x-php', 'application/x-httpd-php-source'],
2692: 'png' => ['image/png', 'image/x-png'],
2693: 'ppt' => ['application/powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.ms-office'],
2694: 'pptx' => ['application/vnd.openxmlformats-officedocument.presentationml.presentation'],
2695: 'psd' => ['application/x-photoshop', 'image/vnd.adobe.photoshop'],
2696: 'ra' => ['audio/x-realaudio'],
2697: 'ram' => ['audio/x-pn-realaudio'],
2698: 'rar' => ['application/x-rar', 'application/rar', 'application/x-rar-compressed'],
2699: 'rpm' => ['audio/x-pn-realaudio-plugin'],
2700: 'rsa' => ['application/x-pkcs7'],
2701: 'rtf' => ['text/rtf'],
2702: 'rtx' => ['text/richtext'],
2703: 'rv' => ['video/vnd.rn-realvideo'],
2704: 'sit' => ['application/x-stuffit'],
2705: 'smil' => ['application/smil'],
2706: 'srt' => ['text/srt'],
2707: 'svg' => ['image/svg+xml'],
2708: 'swf' => ['application/x-shockwave-flash'],
2709: 'tar' => ['application/x-tar'],
2710: 'tgz' => ['application/x-gzip-compressed'],
2711: 'tiff' => ['image/tiff'],
2712: 'ttf' => ['ont/ttf'],
2713: 'txt' => ['text/plain'],
2714: 'vcf' => ['text/x-vcard'],
2715: 'vlc' => ['application/videolan'],
2716: 'vtt' => ['text/vtt'],
2717: 'wav' => ['audio/x-wav', 'audio/wave', 'audio/wav'],
2718: 'wbxml' => ['application/wbxml'],
2719: 'webm' => ['video/webm'],
2720: 'webp' => ['image/webp'],
2721: 'wma' => ['audio/x-ms-wma'],
2722: 'wmlc' => ['application/wmlc'],
2723: 'wmv' => ['video/x-ms-wmv', 'video/x-ms-asf'],
2724: 'woff' => ['font/woff', 'font/woff2'],
2725: 'xhtml' => ['application/xhtml+xml'],
2726: 'xl' => ['application/excel'],
2727: 'xls' => ['application/msexcel', 'application/x-msexcel', 'application/x-ms-excel', 'application/x-excel', 'application/x-dos_ms_excel', 'application/xls', 'application/x-xls'],
2728: 'xlsx' => ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel'],
2729: 'xml' => ['application/xml', 'text/xml'],
2730: 'xsl' => ['text/xsl'],
2731: 'xspf' => ['application/xspf+xml'],
2732: 'z' => ['application/x-compress'],
2733: 'zip' => ['application/x-zip', 'application/zip', 'application/x-zip-compressed', 'application/s-compressed', 'multipart/x-zip'],
2734: 'zsh' => ['text/x-scriptzsh'],
2735: ];
2736:
2737: $result = array_filter($mimeMap, function($val) use ($mime) {
2738: return in_array($mime, $val);
2739: });
2740:
2741: return count($result) ? array_keys($result)[0] : null;
2742: }
2743: