Source of file Filesystem.php

Size: 20,354 Bytes - Last Modified: 2016-05-18T03:08:27+02:00

buildproject/core/module_system/system/Filesystem.php

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
<?php
/*"******************************************************************************************************
*   (c) 2004-2006 by MulchProductions, www.mulchprod.de                                                 *
*   (c) 2007-2016 by Kajona, www.kajona.de                                                              *
*       Published under the GNU LGPL v2.1, see /system/licence_lgpl.txt                                 *
********************************************************************************************************/

namespace Kajona\System\System;


/**
 * Class handling communication with the filesystem, e.g. to read directories
 *
 * @package module_system
 * @author sidler@mulchprod.de
 */
class Filesystem
{

    /**
     * @var null|resource
     */
    private $objFilePointer = null;


    /**
     * Adds the realpath to a given path if not already present
     * @param $strPath
     *
     * @return string
     */
    private function prependRealpath($strPath)
    {
        if (\Kajona\System\System\StringUtil::indexOf(StringUtil::replace("\\", "/", $strPath), _realpath_, false) === false) {
            $strPath = _realpath_.$strPath;
        }

        return $strPath;
    }


    /**
     * Returns all files listed in the passed folder
     *
     * @param string $strFolder
     * @param array $arrSuffix
     * @param bool $bitRecursive
     *
     * @return string[]
     */
    public function getFilelist($strFolder, $arrSuffix = array(), $bitRecursive = false)
    {

        if (!is_array($arrSuffix)) {
            $arrSuffix = array($arrSuffix);
        }

        $arrReturn = array();
        $this->getFilelistHelper($strFolder, $arrSuffix, $bitRecursive, $arrReturn);

        //sorting
        asort($arrReturn);
        return $arrReturn;
    }

    /**
     * Internal helper to load folder contents recursively
     *
     * @param string $strFolder
     * @param string[] $arrSuffix
     * @param bool $bitRecursive
     * @param array &$arrReturn
     *
     * @return void
     */
    private function getFilelistHelper($strFolder, $arrSuffix, $bitRecursive, &$arrReturn)
    {
        $strFolder = $this->prependRealpath($strFolder);

        if (!is_dir($strFolder)) {
            return;
        }

        $arrFiles = scandir($strFolder);
        foreach ($arrFiles as $strFilename) {
            if ($strFilename == "." || $strFilename == "..") {
                continue;
            }

            if (is_file($strFolder."/".$strFilename)) {
                //Wanted Type?
                if (count($arrSuffix) == 0) {
                    $arrReturn[$strFolder."/".$strFilename] = $strFilename;
                }
                else {
                    //check, if suffix is in allowed list
                    $strFileSuffix = uniSubstr($strFilename, uniStrrpos($strFilename, "."));
                    if (in_array($strFileSuffix, $arrSuffix)) {
                        $arrReturn[$strFolder."/".$strFilename] = $strFilename;
                    }
                }
            }
            elseif (is_dir($strFolder."/".$strFilename) && $bitRecursive) {
                $this->getFilelistHelper($strFolder."/".$strFilename, $arrSuffix, $bitRecursive, $arrReturn);
            }
        }

    }


    /**
     * Returns all files an folders in the passed folder
     *
     * @param string $strFolder
     * @param mixed $arrTypes
     * @param mixed $arrExclude
     * @param mixed $arrExcludeFolders
     * @param bool $bitFolders
     * @param bool $bitFiles
     *
     * @return mixed
     */
    public function getCompleteList($strFolder, $arrTypes = array(), $arrExclude = array(), $arrExcludeFolders = array(".", ".."), $bitFolders = true, $bitFiles = true)
    {
        $arrReturn = array("nrFiles"   => 0,
                           "nrFolders" => 0,
                           "files"     => array(),
                           "folders"   => array()
        );


        $strFolder = $this->prependRealpath($strFolder);


        //Valid dir?
        if (is_dir($strFolder)) {
            $objFileHandle = opendir($strFolder);
            if ($objFileHandle !== false) {
                while (($strEntry = readdir($objFileHandle)) !== false) {
                    //Folder
                    if (is_dir($strFolder."/".$strEntry) && $bitFolders == true) {
                        //Folder excluded?
                        if (count($arrExcludeFolders) == 0 || !in_array($strEntry, $arrExcludeFolders)) {
                            $arrReturn["folders"][$arrReturn["nrFolders"]++] = $strEntry;
                        }
                    }

                    //File
                    if (is_file($strFolder."/".$strEntry) && $bitFiles == true) {
                        $arrTemp = $this->getFileDetails($strFolder."/".$strEntry);
                        //Excluded?
                        if (count($arrExclude) == 0 || !in_array($arrTemp["filetype"], $arrExclude)) {
                            //Types given?
                            if (count($arrTypes) != 0) {
                                if (in_array($arrTemp["filetype"], $arrTypes)) {

                                    $arrReturn["files"][$arrReturn["nrFiles"]++] = $arrTemp;
                                }
                            }
                            else {
                                $arrReturn["files"][$arrReturn["nrFiles"]++] = $arrTemp;
                            }
                        }
                    }
                }

            }
            closedir($objFileHandle);
        }

        //sort array
        asort($arrReturn["files"]);
        asort($arrReturn["folders"]);
        return $arrReturn;
    }

    /**
     * Returns detailed info about a file
     *
     * @param string $strFile
     *
     * @return mixed
     */
    public function getFileDetails($strFile)
    {
        $arrReturn = array();

        $strFile = $this->prependRealpath($strFile);

        if (is_file($strFile)) {
            //Filename
            $arrReturn["filename"] = basename($strFile);

            //Type
            $intTemp = uniStrrpos($strFile, ".");
            if ($intTemp !== false) {
                $arrReturn["filetype"] = uniSubstr($strFile, $intTemp);
            }
            else {
                $arrReturn["filetype"] = $strFile;
            }
            $arrReturn["filetype"] = uniStrtolower($arrReturn["filetype"]);
            //Size
            $arrReturn["filesize"] = filesize($strFile);
            //creatipn
            $arrReturn["filecreation"] = filemtime($strFile);
            //change
            $arrReturn["filechange"] = filectime($strFile);
            //access
            $arrReturn["fileaccess"] = fileatime($strFile);
            //path
            $arrReturn["filepath"] = $strFile;
        }

        return $arrReturn;
    }

    /**
     * Renames a file
     *
     * @param string $strSource
     * @param string $strTarget
     * @param bool $bitForce
     *
     * @return bool
     */
    public function fileRename($strSource, $strTarget, $bitForce = false)
    {
        $bitReturn = false;

        $strSource = $this->prependRealpath($strSource);

        if (is_file($strSource)) {
            //bitForce: overwrite existing file
            if (!is_file($strTarget) || $bitForce) {
                $bitReturn = rename($strSource, $strTarget);
            }
        }
        return $bitReturn;
    }

    /**
     * Copies a file
     *
     * @param string $strSource
     * @param string $strTarget
     * @param bool $bitForce
     *
     * @return bool
     */
    public function fileCopy($strSource, $strTarget, $bitForce = false)
    {
        $bitReturn = false;
        $strSource = $this->prependRealpath($strSource);
        $strTarget = $this->prependRealpath($strTarget);

        if (is_file($strSource)) {
            //bitForce: overwrite existing file
            if (!is_file($strTarget) || $bitForce) {

                if(!is_dir(dirname($strTarget))) {
                    $this->folderCreate(dirname($strTarget), true);
                }

                $bitReturn = copy($strSource, $strTarget);
                //set correct rights
                @chmod($strTarget, 0777);
            }
        }
        return $bitReturn;
    }

    /**
     * Deletes a file from the filesystem
     *
     * @param string $strFile
     *
     * @return bool
     */
    public function fileDelete($strFile)
    {
        $strFile = $this->prependRealpath($strFile);
        $bitReturn = false;
        if (is_file($strFile)) {
            $bitReturn = unlink($strFile);
        }
        return $bitReturn;
    }

    /**
     * Deletes a folder from the filesystem
     *
     * @param string $strFolder
     *
     * @return bool
     */
    public function folderDelete($strFolder)
    {
        $bitReturn = false;
        $strFolder = $this->prependRealpath($strFolder);

        if (is_dir($strFolder)) {
            $bitReturn = rmdir($strFolder);
        }

        return $bitReturn;
    }

    /**
     * Deletes a folder and all its contents
     *
     * @param string $strFolder
     *
     * @return bool
     */
    public function folderDeleteRecursive($strFolder)
    {
        $bitReturn = true;
        $arrContents = $this->getCompleteList($strFolder, array(), array(), array(".", ".."));

        foreach ($arrContents["folders"] as $strOneFolder) {
            $bitReturn = $bitReturn && $this->folderDeleteRecursive($strFolder."/".$strOneFolder);
        }

        foreach ($arrContents["files"] as $strOneFile) {
            $bitReturn = $bitReturn && $this->fileDelete($strFolder."/".$strOneFile["filename"]);
        }

        $bitReturn = $bitReturn && $this->folderDelete($strFolder);


        return $bitReturn;
    }

    /**
     * Copies a folder recursive, including all files and folders
     *
     * @param $strSourceDir
     * @param $strTargetDir
     * @param bool $bitOverwrite
     *
     * @since 4.0
     */
    public function folderCopyRecursive($strSourceDir, $strTargetDir, $bitOverwrite = false)
    {

        $strSourceDir = $this->prependRealpath($strSourceDir);
        $strTargetDir = $this->prependRealpath($strTargetDir);

        $arrEntries = scandir($strSourceDir);
        foreach ($arrEntries as $strOneEntry) {
            if ($strOneEntry == "." || $strOneEntry == "..") {
                continue;
            }

            if (is_file($strSourceDir."/".$strOneEntry) && ($bitOverwrite || !is_file($strTargetDir."/".$strOneEntry))) {

                if (!is_dir($strTargetDir)) {
                    mkdir($strTargetDir, 0777, true);
                }

                copy($strSourceDir."/".$strOneEntry, $strTargetDir."/".$strOneEntry);
            }
            elseif (is_dir($strSourceDir."/".$strOneEntry)) {
                if (!is_dir($strTargetDir."/".$strOneEntry)) {
                    mkdir($strTargetDir."/".$strOneEntry, 0777, true);
                }

                $this->folderCopyRecursive($strSourceDir."/".$strOneEntry, $strTargetDir."/".$strOneEntry, $bitOverwrite);
            }
        }
    }

    /**
     * Creates a folder in the filesystem. Use $bitRecursive if you want to create a whole folder tree
     *
     * @param string $strFolder
     * @param bool $bitRecursive
     * @param bool $bitThrowExceptionOnError
     *
     * @return bool
     * @throws Exception
     */
    public function folderCreate($strFolder, $bitRecursive = false, $bitThrowExceptionOnError = false)
    {
        $bitReturn = true;

        $strFolder = $this->prependRealpath($strFolder);

        if (!is_dir($strFolder)) {
            $bitReturn = @mkdir($strFolder, 0777, $bitRecursive);

            if(!$bitReturn && $bitThrowExceptionOnError) {
                throw new Exception("Error creating folder ".$strFolder.", maybe the target is not writable?", Exception::$level_ERROR);
            }
        }

        return $bitReturn;
    }

    /**
     * Fetches the size of a folder recursively
     *
     * @param string $strFolder
     * @param mixed $arrTypes
     * @param mixed $arrExclude
     * @param mixed $arrExcludeFolders
     *
     * @return int
     */
    public function folderSize($strFolder, $arrTypes = array(), $arrExclude = array(), $arrExcludeFolders = array(".svn", ".", ".."))
    {
        $intReturn = 0;

        $arrFiles = $this->getCompleteList($strFolder, $arrTypes, $arrExclude, $arrExcludeFolders);

        foreach ($arrFiles["files"] as $arrFile) {
            $intReturn += $arrFile["filesize"];
        }

        //Call it recursive
        if (count($arrFiles["folders"]) > 0) {
            foreach ($arrFiles["folders"] as $strOneFolder) {
                $intReturn += $this->folderSize($strFolder."/".$strOneFolder, $arrTypes, $arrExclude, $arrExcludeFolders);
            }
        }
        return $intReturn;
    }

    /**
     * Moves an uploaded file
     *
     * @param string $strTarget
     * @param string $strTempfile
     *
     * @return bool
     */
    public function copyUpload($strTarget, $strTempfile)
    {
        $bitReturn = false;
        $strTarget = $this->prependRealpath($strTarget);
        if (is_uploaded_file($strTempfile)) {
            if (@move_uploaded_file($strTempfile, $strTarget)) {
                @unlink($strTempfile);
                //set correct rights
                @chmod($strTarget, 0777);
                $bitReturn = true;
            }
            else {
                @unlink($strTempfile);
            }
        }
        return $bitReturn;
    }

    /**
     * Opens the pointer to a file, used to read from it ot to write to this file
     *
     * @param string $strFilename
     * @param string $strMode w = write, r = read
     *
     * @return bool
     */
    public function openFilePointer($strFilename, $strMode = "w")
    {
        $strFilename = $this->prependRealpath($strFilename);
        $this->objFilePointer = @fopen($strFilename, $strMode);
        if ($this->objFilePointer) {
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * Closes the filepointer currently opened and releases the pointer
     */
    public function closeFilePointer()
    {
        if ($this->objFilePointer != null) {
            @fclose($this->objFilePointer);
        }

        $this->objFilePointer = null;
    }

    /**
     * Sets the current filepointer to a given offset
     *
     * @param int $intOffset
     */
    public function setFilePointerOffset($intOffset)
    {
        if ($this->objFilePointer != null) {
            @fseek($this->objFilePointer, $intOffset);
        }
    }

    /**
     * Tries to write the content passed to the file opened before
     *
     * @param string $strContent
     *
     * @return bool
     */
    public function writeToFile($strContent)
    {
        if ($this->objFilePointer != null) {
            if (@fwrite($this->objFilePointer, $strContent) !== false) {
                return true;
            }
        }
        return false;
    }

    /**
     * Reads a line from the file opened by Filesystem::openFilePointer(name, "r")
     *
     * @return string or false if eof or error
     */
    public function readLineFromFile()
    {
        $strContent = false;

        if ($this->objFilePointer != null) {
            if (!feof($this->objFilePointer)) {
                $strContent = trim(fgets($this->objFilePointer));
            }
        }

        return $strContent;
    }

    /**
     * Reads a section from the end of a file.
     * This is done with pointers, reducing the amount of memory consumed.
     * Open the file by openFilePointer() before.
     *
     * @param int $intNrOfLines
     *
     * @return string
     */
    public function readLastLinesFromFile($intNrOfLines = 10)
    {
        $strReturn = "";
        $intCursor = -1;
        $intLinesRead = 0;

        if ($this->objFilePointer != null) {
            @fseek($this->objFilePointer, $intCursor, SEEK_END);
            $strChar = @fgetc($this->objFilePointer);

            while ($strChar !== false && $intLinesRead <= $intNrOfLines) {
                $strReturn = $strChar.$strReturn;

                @fseek($this->objFilePointer, $intCursor--, SEEK_END);
                $strChar = fgetc($this->objFilePointer);

                if ($strChar == "\n") {
                    $intLinesRead++;
                }
            }
        }

        return $strReturn;
    }

    /**
     * Checks if a file or folder is writable
     *
     * @param string $strFile
     *
     * @return bool
     */
    public function isWritable($strFile)
    {
        return is_writable($this->prependRealpath($strFile));
    }

    /**
     * Wrapper to phps' chmod function. Provides an optional recursion.
     * When called with no other param then the path, a default set of
     *  0644 for files and
     *  0755 for directories
     * is set.
     *
     * @param $strPath
     * @param int $intModeFile
     * @param int $intModeDirectory
     * @param bool $bitRecursive
     *
     * @since 4.0
     * @return bool
     */
    public function chmod($strPath, $intModeFile = 0644, $intModeDirectory = 0755, $bitRecursive = false)
    {
        $strPath = $this->prependRealpath($strPath);
        if (!file_exists($strPath)) {
            return false;
        }


        $bitReturn = @chmod(
            $strPath,
            (is_dir($strPath) ? $intModeDirectory : $intModeFile)
        );

        if ($bitRecursive && is_dir($strPath)) {
            $arrFiles = $this->getCompleteList($strPath);

            foreach ($arrFiles["files"] as $strOneFile) {
                $bitReturn = $bitReturn && chmod($strPath."/".$strOneFile, $intModeFile);
            }
            foreach ($arrFiles["folders"] as $strOneFolder) {
                $bitReturn = $bitReturn && $this->chmod($strPath."/".$strOneFolder, $intModeFile, $intModeDirectory, $bitRecursive);
            }
        }
        return $bitReturn;
    }

    /**
     * Streams the file directly to the client.
     * Make sure to die() the process afterwards, this is not done by this method!
     *
     * @param $strSourceFile
     */
    public function streamFile($strSourceFile)
    {
        $strSourceFile = $this->prependRealpath($strSourceFile);

        //Send the data to the browser
        $strBrowser = getServer("HTTP_USER_AGENT");
        //Check the current browsertype
        if (StringUtil::indexOf($strBrowser, "IE") !== false) {
            //Internet Explorer
            ResponseObject::getInstance()->addHeader("Content-type: application/x-ms-download");
            ResponseObject::getInstance()->addHeader("Content-type: x-type/subtype\n");
            ResponseObject::getInstance()->addHeader("Content-type: application/force-download");
            ResponseObject::getInstance()->addHeader(
                "Content-Disposition: attachment; filename=".preg_replace(
                    '/\./', '%2e',
                    saveUrlEncode(trim(basename($strSourceFile))), substr_count(basename($strSourceFile), '.') - 1
                )
            );
        }
        else {
            //Good: another browser vendor
            ResponseObject::getInstance()->addHeader("Content-Type: application/octet-stream");
            ResponseObject::getInstance()->addHeader("Content-Disposition: attachment; filename=".saveUrlEncode(trim(basename($strSourceFile))));
        }
        //Common headers
        ResponseObject::getInstance()->addHeader("Expires: Mon, 01 Jan 1995 00:00:00 GMT");
        ResponseObject::getInstance()->addHeader("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
        ResponseObject::getInstance()->addHeader("Pragma: no-cache");
        ResponseObject::getInstance()->addHeader("Content-description: JustThum-Generated Data\n");
        ResponseObject::getInstance()->addHeader("Content-Length: ".filesize($strSourceFile));

        //End Session
        Carrier::getInstance()->getObjSession()->sessionClose();

        ob_clean();
        ResponseObject::getInstance()->sendHeaders();

        //Loop the file
        $ptrFile = @fopen($strSourceFile, 'rb');
        fpassthru($ptrFile);
        @fclose($ptrFile);
        ob_flush();
        flush();
    }
}