icingaweb2/library/Icinga/Util/File.php

180 lines
3.9 KiB
PHP

<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Util;
use Exception;
use Icinga\Exception\ProgrammingError;
/**
* File
*
* A class to ease opening files and reading/writing to them.
*/
class File
{
/**
* The location of the file
*
* @var string
*/
protected $path;
/**
* The file resource
*
* @var resource
*/
protected $handle;
/**
* The file access mode to set
*
* Gets set/updated after the file has been closed.
*
* @var int
*/
protected $accessMode;
/**
* Open a file
*
* @param string $path The location of the file
* @param int $openMode The open mode to use
*/
protected function __construct($path, $openMode)
{
$this->setupErrorHandler();
$this->handle = fopen($path, $openMode);
$this->resetErrorHandler();
}
/**
* Open a file
*
* @param string $path The location of the file
* @param int $openMode The open mode to use
*
* @return File
*/
public static function open($path, $openMode = 'r')
{
return new static($path, $openMode);
}
/**
* Read contents of file
*
* @param int|null $length Read up to $length bytes or until EOF if null
*
* @return string
*/
public function read($length = null)
{
$this->setupErrorHandler();
$content = stream_get_contents($this->handle, $length !== null ? $length : -1);
$this->resetErrorHandler();
return $content;
}
/**
* Read file line by line
*
* @return array
*/
public function readlines()
{
$lines = array();
while (($line = fgets($this->handle)) !== false) {
$lines[] = $line;
}
return $lines;
}
/**
* Write contents to file
*
* @param string $bytes The contents to write
*
* @return self
*/
public function write($bytes)
{
$this->setupErrorHandler();
$written = 0;
while ($bytes) {
$justWritten = fwrite($this->handle, $bytes);
if ($justWritten === 0) {
throw new Exception('Failed to write to open file.');
}
$bytes = substr($bytes, $justWritten);
$written += $justWritten;
}
$this->resetErrorHandler();
return $this;
}
/**
* Change access mode of file
*
* Note that the access mode cannot be changed until the file is still open.
*
* @param int $accessMode The access mode to set
*
* @return self
*/
public function chmod($accessMode)
{
$this->accessMode = $accessMode;
return $this;
}
/**
* Close the file
*
* @throws ProgrammingError In case the file is already closed or its resource became invalid
*/
public function close()
{
if (!is_resource($this->handle)) {
throw new ProgrammingError('Tried to close an invalid file resource or an already closed file');
}
fclose($this->handle);
if ($this->accessMode !== null) {
$this->setupErrorHandler();
chmod($this->path, $this->accessMode);
$this->resetErrorHandler();
}
}
/**
* Setup an error handler that throws an exception for every emitted E_WARNING
*/
protected function setupErrorHandler()
{
set_error_handler(
function ($errno, $errstr) {
restore_error_handler(); // Should we call resetErrorHandler() here? (Requires it to be public)
throw new Exception($errstr);
},
E_WARNING
);
}
/**
* Reset the error handler to the system default
*/
protected function resetErrorHandler()
{
restore_error_handler();
}
}