1: <?php
2: /*****************************************************************************************
3: * X2Engine Open Source Edition is a customer relationship management program developed by
4: * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
5: *
6: * This program is free software; you can redistribute it and/or modify it under
7: * the terms of the GNU Affero General Public License version 3 as published by the
8: * Free Software Foundation with the addition of the following permission added
9: * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10: * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
11: * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12: *
13: * This program is distributed in the hope that it will be useful, but WITHOUT
14: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15: * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
16: * details.
17: *
18: * You should have received a copy of the GNU Affero General Public License along with
19: * this program; if not, see http://www.gnu.org/licenses or write to the Free
20: * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21: * 02110-1301 USA.
22: *
23: * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
24: * California 95067, USA. or at email address contact@x2engine.com.
25: *
26: * The interactive user interfaces in modified source and object code versions
27: * of this program must display Appropriate Legal Notices, as required under
28: * Section 5 of the GNU Affero General Public License version 3.
29: *
30: * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31: * these Appropriate Legal Notices must retain the display of the "Powered by
32: * X2Engine" logo. If the display of the logo is not reasonably feasible for
33: * technical reasons, the Appropriate Legal Notices must display the words
34: * "Powered by X2Engine".
35: *****************************************************************************************/
36:
37: /**
38: * Originally a copy of FileUtil.php to remove app's dependencies on updater
39: *
40: * Miscellaneous file system utilities. It is not a child class of CComponent or
41: * the like in order to be portable/stand-alone (so it can be used outside the
42: * app, i.e. by the installer).
43: *
44: * @package application.components.util
45: * @author Demitri Morgan <demitri@x2engine.com>
46: */
47: class AppFileUtil {
48:
49: const ERR_FTPOPER = 100;
50: const ERR_SCPOPER = 200;
51:
52: public static $_finfo;
53:
54: public static $alwaysCurl = false;
55: /**
56: * @var bool $neverCurl overrides $alwaysCurl when true
57: */
58: public static $neverCurl = false;
59:
60: public static $fileOper = "php";
61: public static $ftpChroot = false;
62: private static $_ftpStream;
63: private static $_sshStream;
64: private static $_sftpStream;
65:
66: /**
67: * Copies a file or directory recursively.
68: *
69: * If the local filesystem directory to where the file will be copied does
70: * not exist yet, it will be created automatically. Furthermore, if a remote
71: * URL is being accessed and allow_url_fopen isn't set, it will attempt to
72: * use CURL instead.
73: *
74: * @param string $source The source file.
75: * @param string $target The destination path.
76: * @param boolean $relTarget Transform the target to a relative path. This
77: * option must be false unless the target is an absolute path.
78: * @param boolean $contents If true, and the source is a directory, its
79: * contents will be copied to the destination; otherwise, the whole directory
80: * will be copied into the destination directory.
81: * @return boolean
82: */
83: public static function ccopy($source, $target, $relTarget = false, $contents = true){
84: $ds = DIRECTORY_SEPARATOR;
85: $remote = (bool) preg_match('%^https?://%', $source);
86: // Normalize target to the form where if it's a directory it doesn't have
87: // a trailing slash, and is platform-agnostic:
88: $target = rtrim(self::rpath($target), $ds);
89: // Make the target into a relative path:
90: if($relTarget && !in_array(self::$fileOper, array('ftp', 'scp')))
91: $target = self::relpath($target);
92: // Safeguard against overwriting files:
93: if(!$remote && is_dir($source) && !is_dir($target) && is_file($target))
94: throw new Exception("Cannot copy a directory ($source) into a file ($target).");
95:
96: // Modify file paths for scp and ftp file operations
97: if (self::$fileOper === 'scp') {
98: $target = self::scpXlateRelpath($target);
99: } elseif (self::$fileOper === 'ftp') {
100: $target = self::ftpStripChroot($target);
101: }
102:
103: // Create parent directories if they don't exist already.
104: //
105: // If a file is being copied: the path to examine for directory creation
106: // is one lower than the target (the bottom-level parent).
107: // If a directory is being copied: the same is true (to not create the
108: // top level node) even though it's a directory, because the target
109: // directory will be created anyway if necessary
110: // If a directory is being copied and $contents is false: it's assumed
111: // that the target is a destination directory and not part of the tree
112: // to be copied.
113: $pathNodes = explode($ds, $target);
114: if($contents)
115: array_pop($pathNodes);
116: for($i = 0; $i <= count($pathNodes); $i++){
117: $parent = implode($ds, array_slice($pathNodes, 0, $i));
118: // If we are using an FTP chroot, prepend the $parent path with the chroot dir
119: // so that is_dir() is accurate.
120: if (self::$fileOper === 'ftp' && self::$ftpChroot !== false && !self::isRelative($parent))
121: $verifyDir = self::$ftpChroot.$parent;
122: else
123: $verifyDir = $parent;
124: if($parent != '' && !is_dir($verifyDir)){
125: self::mkdir ($parent);
126: }
127: }
128:
129: if($remote){
130: if(self::tryCurl($source)){
131: // Fall back on the getContents method, which will try using CURL
132: $ch = self::curlInit($source);
133: $contents = curl_exec($ch);
134: if((bool) $contents)
135: return @file_put_contents($target, $contents) !== false;
136: else
137: return false;
138: } else{
139: $context = stream_context_create(array(
140: 'http' => array(
141: 'timeout' => 15 // Timeout in seconds
142: )));
143: return @copy($source, $target, $context) !== false;
144: }
145: }else{
146: // Recursively copy a whole folder
147: $source = self::rpath($source);
148: if(!is_dir($source) && !file_exists($source))
149: throw new Exception("Source file/directory to be copied ($source) not found.");
150:
151: if(is_dir($source)){
152: if(!$contents){
153: // Append the bottom level node in the source path to the
154: // target path.
155: //
156: // This ensures that we copy in the aptly-named target
157: // directory instead of dumping the contents of the source
158: // into the designated target.
159: $source = rtrim($source, $ds);
160: $sourceNodes = explode($ds, $source);
161: $target = $target.$ds.array_pop($sourceNodes);
162: }
163: if(!is_dir($target)){
164: self::mkdir ($target);
165: }
166: $return = true;
167: $files = scandir($source);
168: foreach($files as $file){
169: if($file != '.' && $file != '..'){
170: // Must be recursively called with $relTarget = false
171: // because if ccopy is called with $relTarget = true,
172: // then at this stage "$target" is already relative,
173: // and the argument passed to relpath must be absolute.
174: // It also must be called with contents=true because
175: // that option, if enabled at lower levels, will create
176: // the parent directory twice.
177: $return = $return && FileUtil::ccopy($source.$ds.$file, $target.$ds.$file);
178: }
179: }
180: return $return;
181: }else{
182: switch (self::$fileOper) {
183: case 'ftp':
184: return @ftp_put(self::$_ftpStream, self::ftpStripChroot($target), $source, FTP_BINARY);
185: case 'scp':
186: return @ssh2_scp_send (self::$_sshStream, $source, self::scpXlateRelpath ($target));
187: case 'php':
188: default:
189: $retVal = @copy($source, $target) !== false;
190: self::caseInsensitiveCopyFix ($source, $target);
191: return $retVal;
192: }
193: }
194: }
195: }
196:
197: /**
198: * Create a new directory according to the current file operation method
199: * @param string $path Path to new directory
200: */
201: public static function mkdir($path) {
202: switch (self::$fileOper) {
203: case 'ftp':
204: if (!@ftp_mkdir(self::$_ftpStream, self::ftpStripChroot($path)))
205: throw new Exception("Failed to create directory $path", self::ERR_FTPOPER);
206: break;
207: case 'scp':
208: if (!ssh2_sftp_mkdir (self::$_sftpStream, self::scpXlateRelpath($path)))
209: throw new Exception("Failed to create directory $path", self::ERR_SCPOPER);
210: break;
211: case 'php':
212: default:
213: if(!@mkdir($path))
214: throw new Exception("Failed to create directory $path");
215: }
216: }
217:
218: /**
219: * To be called after copying a file. If it's the case that source and target basenames differ
220: * by case, target will be renamed so that its basename matches the source's. Allows case
221: * of source filename to be preserved in case insensitive file systems.
222: * @return bool false if rename wasn't called, true otherwise (value used for testing purposes)
223: */
224: private static function caseInsensitiveCopyFix ($source, $target) {
225: $sourceBasename = basename ($source);
226: $targetBasename = basename ($target);
227: // if basename of source and target params aren't the same, it means that case was changed
228: // explicitly
229: if ($sourceBasename !== $targetBasename) return false;
230:
231: // get path to file corresponding to target, so that we can get the basename of the actual
232: // file
233: $target = realpath ($target);
234: if (!$target) return false;
235:
236: $targetBasename = basename ($target);
237:
238: // source and target have the same case so renaming won't be necessary
239: if ($targetBasename === $sourceBasename ||
240: // or source and target base name differ by something other than case
241: strtolower ($targetBasename) !== strtolower ($sourceBasename)) {
242:
243: return false;
244: }
245:
246: // replace target basename with source basename
247: $newTargetName = preg_replace (
248: '/'.preg_quote ($targetBasename).'$/', $sourceBasename, $target);
249: if ($newTargetName !== $target) {
250: @rename ($target, $newTargetName);
251: return true;
252: }
253: return false;
254: }
255:
256: /**
257: * Change to a given directory relative to the FTP stream's
258: * current working directory
259: * @param string $target
260: */
261: public static function cdftp($target) {
262: $target = self::ftpStripChroot($target);
263: $src = ftp_pwd(self::$_ftpStream);
264: if ($src === '/')
265: $cd = $target;
266: else {
267: $cd = self::relpath($target, $src . DIRECTORY_SEPARATOR);
268: if (empty($cd))
269: return;
270: }
271: if (!@ftp_chdir(self::$_ftpStream, $cd))
272: throw new Exception("Unable to change to directory '$cd' from '$src'", self::ERR_FTPOPER);
273: }
274:
275: /**
276: * Removes DOS-related junk from an absolute path.
277: *
278: * Returns the path as an array of nodes.
279: */
280: public static function cleanDosPath($path){
281: $a_dirty = explode('\\', $path);
282: $a = array();
283: foreach($a_dirty as $node){
284: $a[] = $node;
285: }
286: $lastNode = array_pop($a);
287: if(preg_match('%/%', $lastNode)){
288: // The final part of the path might contain a relative path put
289: // together with forward slashes (for the lazy developer)
290: foreach(explode('/', $lastNode) as $node)
291: $a[] = $node;
292: }else{
293: $a[] = $lastNode;
294: }
295: return $a;
296: }
297:
298:
299: /**
300: * Create a lockfile using the appropriate functionality, depending
301: * on the selected file operation.
302: * @param String $lockfile
303: */
304: public static function createLockfile($lockfile) {
305: switch (self::$fileOper) {
306: case 'ftp':
307: $stream = fopen('data://text/plain,'.time(), 'r');
308: if (!@ftp_fput(self::$_ftpStream, self::ftpStripChroot($lockfile), $stream, FTP_BINARY))
309: throw new Exception("Unable to create lockfile $lockfile", self::ERR_FTPOPER);
310: fclose($stream);
311: break;
312: case 'scp':
313: $stream = @fopen ('ssh2.sftp://'.ssh2_sftp(self::$_sshStream).$lockfile, 'w');
314: if (!@fwrite ($stream, time()))
315: throw new Exception("Unable to create lockfile $lockfile", self::ERR_SCPOPER);
316: fclose ($stream);
317: break;
318: case 'php':
319: default:
320: file_put_contents($lockfile, time());
321: }
322: }
323:
324: /**
325: * Initializes and returns a CURL resource handle
326: * @param string $url
327: * @param resource|null $context
328: * @return resource
329: */
330: public static function curlInit($url){
331: $ch = curl_init($url);
332: $curlopt = array(
333: CURLOPT_RETURNTRANSFER => 1,
334: CURLOPT_BINARYTRANSFER => 1,
335: CURLOPT_POST => 0,
336: CURLOPT_TIMEOUT => 15
337: );
338: curl_setopt_array($ch, $curlopt);
339: return $ch;
340: }
341:
342: /**
343: * Closes the current FTP stream and resets the file
344: * operation method to 'php'
345: */
346: public static function ftpClose() {
347: ftp_close(self::$_ftpStream);
348: self::$ftpChroot = false;
349: self::$fileOper = 'php';
350: }
351:
352: /**
353: * Initializes the FTP functionality. This connects to
354: * the FTP server, logs in, and sets PASV mode.
355: * Optionally, you can specify the chroot directory and a directory to
356: * change to, e.g.: the web root or the test directory. This is recommended
357: * if working with relative paths.
358: * @param String $host The FTP server to connect to
359: * @param String $user The FTP user.
360: * @param String $pass Specified FTP user's password.
361: * @param String $dir Initial directory to change to, or null by default
362: * to disable.
363: * @param String $chroot The chosen chroot directory for the user.
364: */
365: public static function ftpInit($host, $user, $pass, $dir = null, $chroot = null) {
366: if (!self::$_ftpStream = ftp_connect($host))
367: throw new Exception("The updater is unable to connect to $host. Please check your FTP connection settings.", self::ERR_FTPOPER);
368: if (!@ftp_login(self::$_ftpStream, $user, $pass))
369: throw new Exception("Unable to login as user $user", self::ERR_FTPOPER);
370: ftp_pasv(self::$_ftpStream, true);
371: if ($chroot !== null)
372: self::$ftpChroot = $chroot;
373: if ($dir !== null)
374: self::cdftp($dir);
375: self::$fileOper = "ftp";
376: }
377:
378: public static function ftpStripChroot($dir) {
379: if (self::$fileOper !== 'ftp' || self::$ftpChroot === false || self::isRelative($dir)) // Don't modify a relative path
380: return $dir;
381: else {
382: $replaced = str_replace(self::$ftpChroot, '', $dir);
383: // Add a leading slash if missing
384: if (!preg_match('/^(\/|\\\)/', $replaced))
385: $replaced = DIRECTORY_SEPARATOR.$replaced;
386: return $replaced;
387: }
388: }
389:
390: /**
391: * Translate a relative path to absolute when using the SCP file operation
392: */
393: public static function scpXlateRelpath($path) {
394: $oldpath = $path;
395: if (preg_match ('/^\.\./', $path)) {
396: $path = preg_replace ('/^\.\./', dirname (getcwd()), $path);
397: } elseif (preg_match ('/^\./', $path)) {
398: exit;
399: $path = preg_replace ('/^\./', dirname (__FILE__), $path);
400: }
401: if ($oldpath !== $path)
402: AuxLib::DebugLog ("Xlating $oldpath to $path");
403: return $path;
404: }
405:
406: /**
407: * Initializes SCP file operation functionality. This connects to
408: * the SSH server and authenticates as the provided user.
409: * @param string $host The SSH server to connect to
410: * @param string $user The SSH user.
411: * @param string $pass Specified SSH user's password.
412: */
413: public static function sshInit($host, $user, $pass) {
414: if (!self::$_sshStream = ssh2_connect ($host, 22))
415: throw new Exception("Unable to connect to $host. Please check your SSH connection settings");
416: if (!ssh2_auth_password (self::$_sshStream, $user, $pass))
417: throw new Exception("Unable to login as user $user");
418: self::$_sftpStream = ssh2_sftp(self::$_sshStream);
419: self::$fileOper = "scp";
420: }
421:
422: /**
423: * Explicitly close the current SSH stream and resets the file
424: * operation method to 'php'
425: */
426: public static function sshClose() {
427: ssh2_exec (self::$_sshStream, 'exit;');
428: self::$_sshStream = null;
429: self::$fileOper = 'php';
430: }
431: /**
432: * Wrapper for file_get_contents that attempts to use CURL if allow_url_fopen is disabled.
433: *
434: * @param string|RequestUtil $source
435: * @param type $url
436: * @return type
437: * @throws Exception
438: */
439: public static function getContents($source, $use_include_path = false, $context = null){
440: if(self::tryCurl(($source instanceof RequestUtil) ? $source->url : $source)){
441: if ($source instanceof RequestUtil) {
442: $ch = $source->getCurlHandle ();
443: } else {
444: $ch = self::curlInit($source, $context);
445: }
446:
447: return @curl_exec($ch);
448: }else{
449: if ($source instanceof RequestUtil) {
450: $context = $source->getStreamContext ();
451: $source = $source->url;
452: }
453:
454: // Use the usual copy method
455: return @file_get_contents($source, $use_include_path, $context);
456: }
457: }
458:
459: /**
460: * Returns whether the given parameter is a relative path
461: * @param string $path
462: * @return boolean Whether the path is relative
463: */
464: public static function isRelative($path) {
465: // Paths that start with .. or a word character, but not a Windows
466: // drive specification (C:\).
467: return preg_match('/^\.\./', $path) || preg_match('/^\w[^:]/', $path);
468: }
469:
470: /**
471: * Removes redundant up-one-level directory traversal from a path.
472: *
473: * Returns an array corresponding to each node in the path, with redundant
474: * directory traversal removed. For example, "items/files/stuff/../things"
475: * will be returned as array("items","files","things"). Note, however, that
476: * "../../stuff/things" will be returned as array("stuff","things"), which
477: * does not accurately reflect the actual path from the original working
478: * directory. The intention of this function was to clean up absolute paths.
479: * @param array $path Path to clean
480: */
481: public static function noTraversal($path){
482: $p2 = array();
483: foreach($path as $node){
484: if($node == '..'){
485: if(count($p2) > 0)
486: array_pop($p2);
487: } else{
488: $p2[] = $node;
489: }
490: }
491: return $p2;
492: }
493:
494: /**
495: * Remove a lockfile using the appropriate functionality, depending
496: * on the selected file operation.
497: * @param String $lockfile
498: */
499: public static function removeLockfile($lockfile) {
500: switch (self::$fileOper) {
501: case 'ftp':
502: $lockfile = self::ftpStripChroot($lockfile);
503: if (!@ftp_delete(self::$_ftpStream, $lockfile))
504: throw new Exception("Unable to delete the lockfile $lockfile", self::ERR_FTPOPER);
505: break;
506: case 'scp':
507: if (!@ssh2_sftp_unlink (ssh2_sftp(self::$_sshStream), $lockfile))
508: throw new Exception("Unable to delete the lockfile $lockfile", self::ERR_SCPOPER);
509: break;
510: case 'php':
511: default:
512: unlink($lockfile);
513: }
514: }
515:
516: /**
517: * Format a path so that it is platform-independent. Doesn't return false
518: * if the path doesn't exist (so unlike realpath() it can be used to create
519: * new files).
520: *
521: * @param string $path
522: * @return string
523: */
524: public static function rpath($path){
525: return implode(DIRECTORY_SEPARATOR, explode('/', $path));
526: }
527:
528: /**
529: * Calculates a relative path between two absolute paths.
530: *
531: * @param string $path The path to which the absolute path should be calculated.
532: * @param string $start The starting path. Must be absolute, if specified, and
533: * must be specified if the path argument is not platform-agnostic.
534: * @param string $ds Directory separator. If the two paths (path and start)
535: * use the (almost) ubiquitous convention of forward slashes, but the
536: * calculation is to take place on a Windows machine, this must be set to
537: * forward slash, so that
538: */
539: public static function relpath($path, $start = null, $ds = DIRECTORY_SEPARATOR){
540: $thisPath = $start === null ? realpath('.').$ds : $start;
541: // Get node arrays for each path:
542: if(preg_match('/^([A-Z]):\\\\/', $thisPath, $match0)){ // Windows environment
543: if(preg_match('/([A-Z]):\\\\/', $path, $match1)){ // Target path is absolute
544: if($match0[1] != $match1[1]) // Source and destination are on different drives. Regurgitate the absolute path.
545: return $path;
546: else{ // Source/destination on same drive.
547: $a1 = self::cleanDosPath($path);
548: array_shift($a1);
549: $a1 = self::noTraversal($a1);
550: }
551: }else{ // Target path is relative
552: $a1 = self::noTraversal(explode($ds, $path));
553: }
554: $a0 = self::cleanDosPath($thisPath);
555: array_shift($a0);
556: $a0 = self::noTraversal($a0);
557: array_pop($a0);
558: }else{ // Unix environment. SO MUCH EASIER.
559: $a0 = self::noTraversal(explode($ds, $thisPath));
560: array_pop($a0);
561: $a1 = self::noTraversal(explode($ds, $path));
562: }
563: // Find out what the paths have in common and calculate the number of levels to traverse up:
564: $l = 0;
565: while($l < count($a0) && $l < count($a1)){
566: if($a0[$l] != $a1[$l])
567: break;
568: $l++;
569: }
570: $lUp = count($a0) - $l;
571: return str_repeat('..'.$ds, $lUp).implode($ds, array_slice($a1, $l));
572: }
573:
574: /**
575: * Recursively remove a directory and all its subdirectories.
576: *
577: * Walks a directory structure, removing files recursively. An optional
578: * exclusion pattern can be included. If a directory contains a file that
579: * matches the exclusion pattern, the directory and its ancestors will not
580: * be deleted.
581: *
582: * @param string $path
583: * @param string $noDelPat PCRE pattern for excluding files in deletion.
584: */
585: public static function rrmdir($path, $noDelPat = null){
586: $useExclude = $noDelPat != null;
587: $special = '/.*\/?\.+\/?$/';
588: $excluded = false;
589: if(!realpath($path))
590: return false;
591: $path = realpath($path);
592: if(filetype($path) == 'dir'){
593: $objects = scandir($path);
594: foreach($objects as $object){
595: if(!preg_match($special, $object)){
596: if($useExclude){
597: if(!preg_match($noDelPat, $object)){
598: $excludeThis = self::rrmdir($path.DIRECTORY_SEPARATOR.$object, $noDelPat);
599: $excluded = $excluded || $excludeThis;
600: }else{
601: $excluded = true;
602: }
603: } else
604: self::rrmdir($path.DIRECTORY_SEPARATOR.$object, $noDelPat);
605: }
606: }
607: reset($objects);
608: if(!$excluded)
609: if(!preg_match($special, $path))
610: switch (self::$fileOper) {
611: case 'ftp':
612: $path = self::ftpStripChroot($path);
613: ftp_rmdir(self::$_ftpStream, $path);
614: break;
615: case 'scp':
616: ssh2_sftp_rmdir (ssh2_sftp(self::$_sshStream), $path);
617: break;
618: case 'php':
619: default:
620: rmdir($path);
621: }
622: } else
623: switch (self::$fileOper) {
624: case 'ftp':
625: $path = self::ftpStripChroot($path);
626: ftp_delete(self::$_ftpStream, $path);
627: break;
628: case 'scp':
629: ssh2_sftp_unlink (ssh2_sftp(self::$_sshStream), $path);
630: break;
631: case 'php':
632: default:
633: unlink($path);
634: }
635: return $excluded;
636: }
637:
638: /**
639: * Create/return finfo resource handle
640: *
641: * @return resource
642: */
643: public static function finfo(){
644: if(!isset(self::$_finfo))
645: if(extension_loaded('fileinfo'))
646: self::$_finfo = finfo_open();
647: else
648: self::$_finfo = false;
649: return self::$_finfo;
650: }
651:
652: /**
653: * Create human-readable size string
654: *
655: * @param type $bytes
656: * @return type
657: */
658: public static function formatSize($bytes, $places = 0){
659: $sz = array('', 'K', 'M', 'G', 'T', 'P');
660: $factor = floor((strlen($bytes) - 1) / 3);
661: return sprintf("%.{$places}f ", $bytes / pow(1024, $factor)).@$sz[$factor]."B";
662: }
663:
664: /**
665: * The criteria for which CURL should be used.
666: * @return type
667: */
668: public static function tryCurl($source){
669: if (self::$neverCurl) return false;
670: $try = preg_match('%^https?://%', $source) && (ini_get('allow_url_fopen') == 0 || self::$alwaysCurl);
671: if($try)
672: if(!extension_loaded('curl'))
673: throw new Exception('No HTTP methods available. Tried accessing a remote object, but allow_url_fopen is not enabled and the CURL extension is missing.', 500);
674: return $try;
675: }
676:
677: /**
678: * Taken from CFileValidator
679: * This method is Copyright (c) 2008-2014 by Yii Software LLC
680: * http://www.yiiframework.com/license/
681: */
682: public static function sizeToBytes($sizeStr)
683: {
684: // get the latest character
685: switch (strtolower(substr($sizeStr, -1)))
686: {
687: case 'm': return (int)$sizeStr * 1048576; // 1024 * 1024
688: case 'k': return (int)$sizeStr * 1024; // 1024
689: case 'g': return (int)$sizeStr * 1073741824; // 1024 * 1024 * 1024
690: default: return (int)$sizeStr; // do nothing
691: }
692: }
693:
694: public static function sizeToMb ($sizeStr) {
695: return self::sizeToBytes ($sizeStr) / 1048576;
696: }
697:
698: }
699:
700: ?>
701: