Ok, while I'm in a thread about memory I may as well through a challenge to you, although I suspect the issue lies with the native base64_encode() function.
Code: Select all
<?php
class Swift_Message_Encoder
{
// *** SNIP ****
/**
* Return the base64 encoded version of a string with no breaks
* @param The input string to encode
* @return string
*/
public function rawBase64Encode($string)
{
return $string = base64_encode($string);
}
/**
* Return the base64 encoded version of a file
* @param Swift_File The file input stream
* @return string
* @throws Swift_FileException If the file cannot be read
*/
public function base64EncodeFile(Swift_File $file, $chunk=76)
{
$ret = "";
//We have to read in multiples of 3 bytes but avoid doing such small chunks that it takes too long
while (false !== $bytes = $file->read(8190)) $ret .= $this->rawBase64Encode($bytes);
$file->reset();
$ret = chunk_split($ret, $chunk-2, "\r\n");
return trim($ret);
}
// **** SNIP ****
}
Code: Select all
<?php
/**
* Swift Mailer File Stream Wrapper
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/FileException.php";
/**
* Swift File stream abstraction layer
* Reads bytes from a file
* @package Swift
* @author Chris Corbyn <chris@w3style.co.uk>
*/
class Swift_File
{
/**
* The accessible path to the file
* @var string
*/
protected $path = null;
/**
* The resource returned by fopen() against the path
* @var resource
*/
protected $handle = null;
/**
* Constructor
* @param string The path the the file
* @throws Swift_FileException If the file cannot be found
*/
public function __construct($path)
{
$this->setPath($path);
}
/**
* Set the path to the file
* @param string The path to the file
* @throws Swift_FileException If the file cannot be found
*/
public function setPath($path)
{
if (!file_exists($path)) throw new Swift_FileException("No such file '" . $path ."'");
$this->path = $path;
}
/**
* Establish an open file handle on the file if the file is not yet opened
* @throws Swift_FileException If the file cannot be opened for reading
*/
protected function createHandle()
{
if ($this->handle === null)
{
if (!$this->handle = fopen($this->path, "rb"))
{
throw new Swift_FileException("Unable to open file '" . $this->path . " for reading");
}
}
}
/**
* Check if the pointer as at the end of the file
* @return boolean
* @throws Swift_FileException If the file cannot be read
*/
public function EOF()
{
$this->createHandle();
return feof($this->handle);
}
/**
* Get a single byte from the file
* Returns false past EOF
* @return string
* @throws Swift_FileException If the file cannot be read
*/
public function getByte()
{
$this->createHandle();
return $this->read(1);
}
/**
* Read one full line from the file including the line ending
* Returns false past EOF
* @return string
* @throws Swift_FileException If the file cannot be read
*/
public function readln()
{
$this->createHandle();
if (!$this->EOF()) return fgets($this->handle);
else return false;
}
/**
* Read a given number of bytes from the file
* Returns false past EOF
* @return string
* @throws Swift_FileException If the file cannot be read
*/
public function read($bytes)
{
$this->createHandle();
if (!$this->EOF()) return fread($this->handle, $bytes);
else return false;
}
/**
* Get the size of the file in bytes
* @return int
*/
public function length()
{
return filesize($this->path);
}
/**
* Close the open handle on the file
* @throws Swift_FileException If the file cannot be read
*/
public function close()
{
$this->createHandle();
fclose($this->handle);
$this->handle = null;
}
/**
* Reset the file pointer back to zero
*/
public function reset()
{
$this->createHandle();
fseek($this->handle, 0);
}
/**
* Destructor
* Closes the file
*/
public function __destruct()
{
if ($this->handle !== null) $this->close();
}
}
If I run this code, with a 500KB (exactly 500KB) file:
Code: Select all
$encoder = new Swift_Message_Encoder();
$encoder->base64EncodeFile(new Swift_File("/home/d11wtq/testfile"));
It uses 1.65MB of memory, yet at any one time from what I can see there's only 8190 bytes from what's read from the file, the resulting base64 encoded chunk of that (~1100 bytes), the full return value (up to 700KB) and nothing else. I've looped over the file without base64 encoding the data an there's no memory issue there so something uses a lot of memory between base64EncodeFile() and rawBase64Encode(). Clues? That really sucks if it's PHP's base64_encode() function using all that memory since it's not exactly a complex algorithm but doing it in PHP code would be a lot slower
I'm not fussed if nobody can adapt my code but I'd love to see somebody get a base64 encoded string from a 500KB file using less memory than 1.65MB. I figured it should be doable with only 8190 + 700000 + {overhead} bytes (around 800KB), but obviously not so.