mirror of
				https://github.com/Icinga/icingaweb2.git
				synced 2025-11-04 05:05:01 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			211 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
 | 
						|
 | 
						|
namespace Icinga\Util;
 | 
						|
 | 
						|
use SplFileObject;
 | 
						|
use ErrorException;
 | 
						|
use RuntimeException;
 | 
						|
use Icinga\Exception\NotWritableError;
 | 
						|
 | 
						|
/**
 | 
						|
 * File
 | 
						|
 *
 | 
						|
 * A class to ease opening files and reading/writing to them.
 | 
						|
 */
 | 
						|
class File extends SplFileObject
 | 
						|
{
 | 
						|
    /**
 | 
						|
     * The mode used to open the file
 | 
						|
     *
 | 
						|
     * @var string
 | 
						|
     */
 | 
						|
    protected $openMode;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The access mode to use when creating directories
 | 
						|
     *
 | 
						|
     * @var int
 | 
						|
     */
 | 
						|
    public static $dirMode = 1528; // 2770
 | 
						|
 | 
						|
    /**
 | 
						|
     * @see SplFileObject::__construct()
 | 
						|
     */
 | 
						|
    public function __construct($filename, $openMode = 'r', $useIncludePath = false, $context = null)
 | 
						|
    {
 | 
						|
        $this->openMode = $openMode;
 | 
						|
        if ($context === null) {
 | 
						|
            parent::__construct($filename, $openMode, $useIncludePath);
 | 
						|
        } else {
 | 
						|
            parent::__construct($filename, $openMode, $useIncludePath, $context);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Create a file using the given access mode and return a instance of File open for writing
 | 
						|
     *
 | 
						|
     * @param   string  $path           The path to the file
 | 
						|
     * @param   int     $accessMode     The access mode to set
 | 
						|
     * @param   bool    $recursive      Whether missing nested directories of the given path should be created
 | 
						|
     *
 | 
						|
     * @return  File
 | 
						|
     *
 | 
						|
     * @throws  RuntimeException        In case the file cannot be created or the access mode cannot be set
 | 
						|
     * @throws  NotWritableError        In case the path's (existing) parent is not writable
 | 
						|
     */
 | 
						|
    public static function create($path, $accessMode, $recursive = true)
 | 
						|
    {
 | 
						|
        $dirPath = dirname($path);
 | 
						|
        if ($recursive && !is_dir($dirPath)) {
 | 
						|
            static::createDirectories($dirPath);
 | 
						|
        } elseif (! is_writable($dirPath)) {
 | 
						|
            throw new NotWritableError(sprintf('Path "%s" is not writable', $dirPath));
 | 
						|
        }
 | 
						|
 | 
						|
        $file = new static($path, 'x+');
 | 
						|
 | 
						|
        if (! @chmod($path, $accessMode)) {
 | 
						|
            $error = error_get_last();
 | 
						|
            throw new RuntimeException(sprintf(
 | 
						|
                'Cannot set access mode "%s" on file "%s" (%s)',
 | 
						|
                decoct($accessMode),
 | 
						|
                $path,
 | 
						|
                $error['message']
 | 
						|
            ));
 | 
						|
        }
 | 
						|
 | 
						|
        return $file;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Create missing directories
 | 
						|
     *
 | 
						|
     * @param   string  $path
 | 
						|
     *
 | 
						|
     * @throws  RuntimeException        In case a directory cannot be created or the access mode cannot be set
 | 
						|
     */
 | 
						|
    protected static function createDirectories($path)
 | 
						|
    {
 | 
						|
        $part = strpos($path, DIRECTORY_SEPARATOR) === 0 ? DIRECTORY_SEPARATOR : '';
 | 
						|
        foreach (explode(DIRECTORY_SEPARATOR, ltrim($path, DIRECTORY_SEPARATOR)) as $dir) {
 | 
						|
            $part .= $dir . DIRECTORY_SEPARATOR;
 | 
						|
 | 
						|
            if (! is_dir($part)) {
 | 
						|
                if (! @mkdir($part, static::$dirMode)) {
 | 
						|
                    $error = error_get_last();
 | 
						|
                    throw new RuntimeException(sprintf(
 | 
						|
                        'Failed to create missing directory "%s" (%s)',
 | 
						|
                        $part,
 | 
						|
                        $error['message']
 | 
						|
                    ));
 | 
						|
                }
 | 
						|
 | 
						|
                if (! @chmod($part, static::$dirMode)) {
 | 
						|
                    $error = error_get_last();
 | 
						|
                    throw new RuntimeException(sprintf(
 | 
						|
                        'Failed to set access mode "%s" for directory "%s" (%s)',
 | 
						|
                        decoct(static::$dirMode),
 | 
						|
                        $part,
 | 
						|
                        $error['message']
 | 
						|
                    ));
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @see SplFileObject::fwrite()
 | 
						|
     */
 | 
						|
    public function fwrite($str, $length = null)
 | 
						|
    {
 | 
						|
        $this->assertOpenForWriting();
 | 
						|
        $this->setupErrorHandler();
 | 
						|
        $retVal = $length === null ? parent::fwrite($str) : parent::fwrite($str, $length);
 | 
						|
        restore_error_handler();
 | 
						|
        return $retVal;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @see SplFileObject::ftruncate()
 | 
						|
     */
 | 
						|
    public function ftruncate($size)
 | 
						|
    {
 | 
						|
        $this->assertOpenForWriting();
 | 
						|
        $this->setupErrorHandler();
 | 
						|
        $retVal = parent::ftruncate($size);
 | 
						|
        restore_error_handler();
 | 
						|
        return $retVal;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @see SplFileObject::ftell()
 | 
						|
     */
 | 
						|
    public function ftell()
 | 
						|
    {
 | 
						|
        $this->setupErrorHandler();
 | 
						|
        $retVal = parent::ftell();
 | 
						|
        restore_error_handler();
 | 
						|
        return $retVal;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @see SplFileObject::flock()
 | 
						|
     */
 | 
						|
    public function flock($operation, &$wouldblock = null)
 | 
						|
    {
 | 
						|
        $this->setupErrorHandler();
 | 
						|
        $retVal = parent::flock($operation, $wouldblock);
 | 
						|
        restore_error_handler();
 | 
						|
        return $retVal;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @see SplFileObject::fgetc()
 | 
						|
     */
 | 
						|
    public function fgetc()
 | 
						|
    {
 | 
						|
        $this->setupErrorHandler();
 | 
						|
        $retVal = parent::fgetc();
 | 
						|
        restore_error_handler();
 | 
						|
        return $retVal;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @see SplFileObject::fflush()
 | 
						|
     */
 | 
						|
    public function fflush()
 | 
						|
    {
 | 
						|
        $this->setupErrorHandler();
 | 
						|
        $retVal = parent::fflush();
 | 
						|
        restore_error_handler();
 | 
						|
        return $retVal;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Setup an error handler that throws a RuntimeException for every emitted E_WARNING
 | 
						|
     */
 | 
						|
    protected function setupErrorHandler()
 | 
						|
    {
 | 
						|
        set_error_handler(
 | 
						|
            function ($errno, $errstr, $errfile, $errline) {
 | 
						|
                restore_error_handler();
 | 
						|
                throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
 | 
						|
            },
 | 
						|
            E_WARNING
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Assert that the file was opened for writing and throw an exception otherwise
 | 
						|
     *
 | 
						|
     * @throws  NotWritableError    In case the file was not opened for writing
 | 
						|
     */
 | 
						|
    protected function assertOpenForWriting()
 | 
						|
    {
 | 
						|
        if (!preg_match('@w|a|\+@', $this->openMode)) {
 | 
						|
            throw new NotWritableError('File not open for writing');
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |