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