blob: 19d14631313fe7a07f6f84f9e80b7c787e8d692f [file] [log] [blame]
<?php
/**
* File contains the ezcArchiveCharacterFile class.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @package Archive
* @version //autogentag//
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @access private
*/
/**
* The ezcArchiveCharacterFile class provides an interface for reading from and writing to a file.
*
* The file is opened via in constructor and closed via the destructor.
*
* The iterator functionality can be used to read characters from and append characters to the file,
* so it has the same behaviour as the {@link ezcArchiveBlockFile}.
*
* @package Archive
* @version //autogentag//
* @access private
*/
class ezcArchiveCharacterFile extends ezcArchiveFile
{
/**
* The current character.
*
* @todo FIXME
*
* @var string
*/
private $character;
/**
* The current character position.
*
* @var int
*/
private $position;
/**
* Sets the property $name to $value.
*
* Because there are no properties available, this method will always
* throw an {@link ezcBasePropertyNotFoundException}.
*
* @throws ezcBasePropertyNotFoundException if the property does not exist.
* @param string $name
* @param mixed $value
* @return void
* @ignore
*/
public function __set( $name, $value )
{
throw new ezcBasePropertyNotFoundException( $name );
}
/**
* Returns the property $name.
*
* Because there are no properties available, this method will always
* throw an {@link ezcBasePropertyNotFoundException}.
*
* @throws ezcBasePropertyNotFoundException if the property does not exist.
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
throw new ezcBasePropertyNotFoundException( $name );
}
/**
* Constructs a new ezcArchiveBlockFile.
*
* The given file name is tried to be opened in read / write mode.
* If that fails, the file will be opened in read-only mode.
*
* If the bool $createIfNotExist is set to true, it will create the file if
* it doesn't exist.
*
* @throws ezcArchiveException if the file cannot be found or opened for any reason.
*
* @param string $fileName
* @param bool $createIfNotExist
* @param bool $readOnly
*/
public function __construct( $fileName, $createIfNotExist = false, $readOnly = false )
{
$this->openFile( $fileName, $createIfNotExist, $readOnly );
$this->rewind();
}
/**
* The destructor will close all open files.
*/
public function __destruct()
{
if ( $this->fp )
{
fclose( $this->fp );
}
}
/**
* Rewinds the current file.
*
* @return void
*/
public function rewind()
{
parent::rewind();
$this->position = -1;
}
/**
* Returns the current character if available.
*
* If the character is not available, the value false is returned.
*
* @return string
*/
public function current()
{
return ( $this->isValid ? $this->character : false );
}
/**
* Iterates to the next character.
*
* Returns the next character if it exists; otherwise returns false.
*
* @return string
*/
public function next()
{
if ( $this->isValid )
{
$this->character = fgetc( $this->fp );
if ( $this->character === false )
{
$this->isValid = false;
return false;
}
$this->position++;
return $this->character;
}
return false;
}
/**
* Returns the current position.
*
* The first position has the value zero.
*
* @return int
*/
public function key()
{
return ( $this->isValid ? ftell( $this->fp ) - 1 : false );
}
/**
* Returns the file-pointer position.
*
* @todo FIXME Is this or the key() function needed?
*
* return int
*/
public function getPosition()
{
return ftell( $this->fp );
}
/**
* Returns true if the current character is valid, otherwise false.
*
* @return bool
*/
public function valid()
{
return $this->isValid;
}
/**
* Appends the string $data after the current position.
*
* The character(s) after the current position are removed and the $data will be
* appended.
* To start from the beginning of the file, call first the truncate() method.
*
* @throws ezcBaseFilePermissionException if the file is opened in read-only mode.
*
* @param string $data
* @return int
*/
public function append( $data )
{
if ( $this->fileAccess == self::READ_ONLY )
{
throw new ezcBaseFilePermissionException( $this->fileName, ezcBaseFilePermissionException::WRITE, "The archive is opened in a read-only mode." );
}
$pos = ftell( $this->fp );
ftruncate( $this->fp, $pos );
$length = $this->writeBytes( $data );
$this->isValid = true;
if ( $this->isEmpty )
{
rewind( $this->fp );
$this->next();
}
else
{
$this->positionSeek( $pos, SEEK_SET );
}
$this->isEmpty = false;
return $length;
}
/**
* Writes the given string $data to the current file.
*
* This method tries to write the $data to the file. Upon failure, this method
* will retry, until no progress is made anymore. And eventually it will throw
* an exception.
*
* @throws ezcBaseFileIoException if it is not possible to write to the file.
*
* @param string $data
* @return void
*/
protected function writeBytes( $data )
{
$dl = strlen( $data );
if ( $dl == 0 )
{
return; // No bytes to write.
}
$wl = fwrite( $this->fp, $data );
// Partly written? For example an interrupt can occur when writing a remote file.
while ( $dl > $wl && $wl != 0 )
{
// retry, until no progress is made.
$data = substr( $data, $wl );
$dl = strlen( $data );
$wl = fwrite( $this->fp, $data );
}
if ( $wl == 0 )
{
throw new ezcBaseFileIoException ( $this->fileName, ezcBaseFileIoException::WRITE, "Retried to write, but no progress was made. Disk full?" );
}
return $wl;
}
/**
* Truncates the current file to the number of characters $position.
*
* If $position is zero, the entire block file will be truncated. After the file is truncated,
* make sure the current block position is valid. So, do a rewind() after
* truncating the entire block file.
*
* @param int $position
* @return void
*/
public function truncate( $position = 0 )
{
ftruncate( $this->fp, $position );
if ( $position == 0 )
{
$this->isEmpty = true;
}
if ( $this->position > $position )
{
$this->isValid = false;
}
}
/**
* Sets the current character position.
*
* Sets the current character position. The new position is obtained by adding
* the $offset amount of characters to the position specified by $whence.
*
* These values are:
* SEEK_SET: The first character,
* SEEK_CUR: The current character position,
* SEEK_END: The last character.
*
* The blockOffset can be negative.
*
* @param int $offset
* @param int $whence
* @return void
*/
public function seek( $offset, $whence = SEEK_SET )
{
$this->isValid = true;
$pos = $offset;
/*
if ( $whence == SEEK_END || $whence == SEEK_CUR )
{
if ( !$this->isEmpty() )
{
$pos -= 1;
}
}
*/
if ( $this->positionSeek( $pos, $whence ) == -1 )
{
$this->isValid = false;
}
$this->position = $pos - 1;
$this->next(); // Will set isValid to false, if blockfile is empty.
}
/**
* Returns true if the file is empty, otherwise false.
*
* @return bool
*/
public function isEmpty()
{
return $this->isEmpty;
}
/**
* Reads current character plus extra. Forward the current pointer.
*
* @param int $bytes
* @return string
*/
public function read( $bytes )
{
if ( $bytes < 1 )
{
return false;
}
$data = $this->character;
if ( $bytes == 1 )
{
$this->next();
return $data;
}
$data .= fread( $this->fp, $bytes - 1 );
$this->position += $bytes - 1;
if ( $data === false )
{
$this->isValid = false;
return false;
}
else
{
$this->next();
}
return $data;
}
/**
* Writes the specified data and returns the length of the written data.
*
* FIXME, maybe valid() when at the eof.
* FIXME. Write current character plus extra.
* FIXME.. slow.
*
* @throws ezcBaseFilePermissionException
* if the file access is read-only
* @param string $data
* @return int
*/
public function write( $data )
{
if ( $this->fileAccess == self::READ_ONLY )
{
throw new ezcBaseFilePermissionException( $this->fileName, ezcBaseFilePermissionException::WRITE, "The archive is opened in a read-only mode." );
}
$pos = ftell( $this->fp );
if ( $this->valid() )
{
$pos--;
}
fseek( $this->fp, $pos );
ftruncate( $this->fp, $pos );
$length = $this->writeBytes( $data );
$this->isValid = false;
$this->isEmpty = false;
return $length;
}
}
?>