Commit a614c779 by Juliper

Bagian III

parent 0ee13881
<?php
/*
* This file is part of Object Enumerator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\ObjectEnumerator;
use SebastianBergmann\ObjectEnumerator\Fixtures\ExceptionThrower;
/**
* @covers SebastianBergmann\ObjectEnumerator\Enumerator
*/
class EnumeratorTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Enumerator
*/
private $enumerator;
protected function setUp()
{
$this->enumerator = new Enumerator;
}
public function testEnumeratesSingleObject()
{
$a = new \stdClass;
$objects = $this->enumerator->enumerate($a);
$this->assertCount(1, $objects);
$this->assertSame($a, $objects[0]);
}
public function testEnumeratesArrayWithSingleObject()
{
$a = new \stdClass;
$objects = $this->enumerator->enumerate([$a]);
$this->assertCount(1, $objects);
$this->assertSame($a, $objects[0]);
}
public function testEnumeratesArrayWithTwoReferencesToTheSameObject()
{
$a = new \stdClass;
$objects = $this->enumerator->enumerate([$a, $a]);
$this->assertCount(1, $objects);
$this->assertSame($a, $objects[0]);
}
public function testEnumeratesArrayOfObjects()
{
$a = new \stdClass;
$b = new \stdClass;
$objects = $this->enumerator->enumerate([$a, $b, null]);
$this->assertCount(2, $objects);
$this->assertSame($a, $objects[0]);
$this->assertSame($b, $objects[1]);
}
public function testEnumeratesObjectWithAggregatedObject()
{
$a = new \stdClass;
$b = new \stdClass;
$a->b = $b;
$a->c = null;
$objects = $this->enumerator->enumerate($a);
$this->assertCount(2, $objects);
$this->assertSame($a, $objects[0]);
$this->assertSame($b, $objects[1]);
}
public function testEnumeratesObjectWithAggregatedObjectsInArray()
{
$a = new \stdClass;
$b = new \stdClass;
$a->b = [$b];
$objects = $this->enumerator->enumerate($a);
$this->assertCount(2, $objects);
$this->assertSame($a, $objects[0]);
$this->assertSame($b, $objects[1]);
}
public function testEnumeratesObjectsWithCyclicReferences()
{
$a = new \stdClass;
$b = new \stdClass;
$a->b = $b;
$b->a = $a;
$objects = $this->enumerator->enumerate([$a, $b]);
$this->assertCount(2, $objects);
$this->assertSame($a, $objects[0]);
$this->assertSame($b, $objects[1]);
}
public function testEnumeratesClassThatThrowsException()
{
$thrower = new ExceptionThrower();
$objects = $this->enumerator->enumerate($thrower);
$this->assertSame($thrower, $objects[0]);
}
public function testExceptionIsRaisedForInvalidArgument()
{
$this->setExpectedException(InvalidArgumentException::class);
$this->enumerator->enumerate(null);
}
public function testExceptionIsRaisedForInvalidArgument2()
{
$this->setExpectedException(InvalidArgumentException::class);
$this->enumerator->enumerate([], '');
}
}
<?php
/*
* This file is part of Object Enumerator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\ObjectEnumerator\Fixtures;
use RuntimeException;
class ExceptionThrower
{
private $property;
public function __construct()
{
unset($this->property);
}
public function __get($property)
{
throw new RuntimeException;
}
}
.idea
phpunit.xml
composer.lock
composer.phar
vendor/
cache.properties
build/LICENSE
build/README.md
build/*.tgz
language: php
php:
- 5.3.3
- 5.3
- 5.4
- 5.5
- 5.6
- hhvm
sudo: false
before_script:
- composer self-update
- composer install --no-interaction --prefer-source --dev
script: ./vendor/bin/phpunit
notifications:
email: false
irc: "irc.freenode.org#phpunit"
Recursion Context
Copyright (c) 2002-2015, Sebastian Bergmann <sebastian@phpunit.de>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Sebastian Bergmann nor the names of his
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
# Recursion Context
...
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
composer require sebastian/recursion-context
If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency:
composer require --dev sebastian/recursion-context
<?xml version="1.0" encoding="UTF-8"?>
<project name="recursion-context">
<target name="clean" description="Cleanup build artifacts">
<delete dir="${basedir}/vendor"/>
<delete file="${basedir}/composer.lock"/>
</target>
<target name="composer" depends="clean" description="Install dependencies with Composer">
<tstamp>
<format property="thirty.days.ago" pattern="MM/dd/yyyy hh:mm aa" offset="-30" unit="day"/>
</tstamp>
<delete>
<fileset dir="${basedir}">
<include name="composer.phar" />
<date datetime="${thirty.days.ago}" when="before"/>
</fileset>
</delete>
<get src="https://getcomposer.org/composer.phar" dest="${basedir}/composer.phar" skipexisting="true"/>
<exec executable="php">
<arg value="composer.phar"/>
<arg value="install"/>
</exec>
</target>
</project>
{
"name": "sebastian/recursion-context",
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
}
],
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"autoload": {
"classmap": [
"src/"
]
},
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
bootstrap="vendor/autoload.php"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
checkForUnintentionallyCoveredCode="true"
forceCoversAnnotation="true"
verbose="true">
<testsuites>
<testsuite name="recursion-context">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true" processUncoveredFilesFromWhitelist="true">
<directory>src</directory>
</whitelist>
</filter>
</phpunit>
<?php
/*
* This file is part of the Recursion Context package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\RecursionContext;
/**
* A context containing previously processed arrays and objects
* when recursively processing a value.
*/
final class Context
{
/**
* @var array[]
*/
private $arrays;
/**
* @var \SplObjectStorage
*/
private $objects;
/**
* Initialises the context
*/
public function __construct()
{
$this->arrays = array();
$this->objects = new \SplObjectStorage;
}
/**
* Adds a value to the context.
*
* @param array|object $value The value to add.
*
* @return int|string The ID of the stored value, either as a string or integer.
*
* @throws InvalidArgumentException Thrown if $value is not an array or object
*/
public function add(&$value)
{
if (is_array($value)) {
return $this->addArray($value);
} elseif (is_object($value)) {
return $this->addObject($value);
}
throw new InvalidArgumentException(
'Only arrays and objects are supported'
);
}
/**
* Checks if the given value exists within the context.
*
* @param array|object $value The value to check.
*
* @return int|string|false The string or integer ID of the stored value if it has already been seen, or false if the value is not stored.
*
* @throws InvalidArgumentException Thrown if $value is not an array or object
*/
public function contains(&$value)
{
if (is_array($value)) {
return $this->containsArray($value);
} elseif (is_object($value)) {
return $this->containsObject($value);
}
throw new InvalidArgumentException(
'Only arrays and objects are supported'
);
}
/**
* @param array $array
*
* @return bool|int
*/
private function addArray(array &$array)
{
$key = $this->containsArray($array);
if ($key !== false) {
return $key;
}
$key = count($this->arrays);
$this->arrays[] = &$array;
if (!isset($array[PHP_INT_MAX]) && !isset($array[PHP_INT_MAX - 1])) {
$array[] = $key;
$array[] = $this->objects;
} else { /* cover the improbable case too */
do {
$key = random_int(PHP_INT_MIN, PHP_INT_MAX);
} while (isset($array[$key]));
$array[$key] = $key;
do {
$key = random_int(PHP_INT_MIN, PHP_INT_MAX);
} while (isset($array[$key]));
$array[$key] = $this->objects;
}
return $key;
}
/**
* @param object $object
*
* @return string
*/
private function addObject($object)
{
if (!$this->objects->contains($object)) {
$this->objects->attach($object);
}
return spl_object_hash($object);
}
/**
* @param array $array
*
* @return int|false
*/
private function containsArray(array &$array)
{
$end = array_slice($array, -2);
return isset($end[1]) && $end[1] === $this->objects ? $end[0] : false;
}
/**
* @param object $value
*
* @return string|false
*/
private function containsObject($value)
{
if ($this->objects->contains($value)) {
return spl_object_hash($value);
}
return false;
}
public function __destruct()
{
foreach ($this->arrays as &$array) {
if (is_array($array)) {
array_pop($array);
array_pop($array);
}
}
}
}
<?php
/*
* This file is part of the Recursion Context package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\RecursionContext;
/**
*/
interface Exception
{
}
<?php
/*
* This file is part of the Recursion Context package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\RecursionContext;
/**
*/
final class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
<?php
/*
* This file is part of the Recursion Context package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\RecursionContext;
use PHPUnit_Framework_TestCase;
/**
* @covers SebastianBergmann\RecursionContext\Context
*/
class ContextTest extends PHPUnit_Framework_TestCase
{
/**
* @var \SebastianBergmann\RecursionContext\Context
*/
private $context;
protected function setUp()
{
$this->context = new Context();
}
public function failsProvider()
{
return array(
array(true),
array(false),
array(null),
array('string'),
array(1),
array(1.5),
array(fopen('php://memory', 'r'))
);
}
public function valuesProvider()
{
$obj2 = new \stdClass();
$obj2->foo = 'bar';
$obj3 = (object) array(1,2,"Test\r\n",4,5,6,7,8);
$obj = new \stdClass();
//@codingStandardsIgnoreStart
$obj->null = null;
//@codingStandardsIgnoreEnd
$obj->boolean = true;
$obj->integer = 1;
$obj->double = 1.2;
$obj->string = '1';
$obj->text = "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext";
$obj->object = $obj2;
$obj->objectagain = $obj2;
$obj->array = array('foo' => 'bar');
$obj->array2 = array(1,2,3,4,5,6);
$obj->array3 = array($obj, $obj2, $obj3);
$obj->self = $obj;
$storage = new \SplObjectStorage();
$storage->attach($obj2);
$storage->foo = $obj2;
return array(
array($obj, spl_object_hash($obj)),
array($obj2, spl_object_hash($obj2)),
array($obj3, spl_object_hash($obj3)),
array($storage, spl_object_hash($storage)),
array($obj->array, 0),
array($obj->array2, 0),
array($obj->array3, 0)
);
}
/**
* @covers SebastianBergmann\RecursionContext\Context::add
* @uses SebastianBergmann\RecursionContext\InvalidArgumentException
* @dataProvider failsProvider
*/
public function testAddFails($value)
{
$this->setExpectedException(
'SebastianBergmann\\RecursionContext\\Exception',
'Only arrays and objects are supported'
);
$this->context->add($value);
}
/**
* @covers SebastianBergmann\RecursionContext\Context::contains
* @uses SebastianBergmann\RecursionContext\InvalidArgumentException
* @dataProvider failsProvider
*/
public function testContainsFails($value)
{
$this->setExpectedException(
'SebastianBergmann\\RecursionContext\\Exception',
'Only arrays and objects are supported'
);
$this->context->contains($value);
}
/**
* @covers SebastianBergmann\RecursionContext\Context::add
* @dataProvider valuesProvider
*/
public function testAdd($value, $key)
{
$this->assertEquals($key, $this->context->add($value));
// Test we get the same key on subsequent adds
$this->assertEquals($key, $this->context->add($value));
}
/**
* @covers SebastianBergmann\RecursionContext\Context::contains
* @uses SebastianBergmann\RecursionContext\Context::add
* @depends testAdd
* @dataProvider valuesProvider
*/
public function testContainsFound($value, $key)
{
$this->context->add($value);
$this->assertEquals($key, $this->context->contains($value));
// Test we get the same key on subsequent calls
$this->assertEquals($key, $this->context->contains($value));
}
/**
* @covers SebastianBergmann\RecursionContext\Context::contains
* @dataProvider valuesProvider
*/
public function testContainsNotFound($value)
{
$this->assertFalse($this->context->contains($value));
}
}
Resource Operations
Copyright (c) 2015, Sebastian Bergmann <sebastian@phpunit.de>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Sebastian Bergmann nor the names of his
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
# Resource Operations
Provides a list of PHP built-in functions that operate on resources.
## Installation
To add this component as a local, per-project dependency to your project, simply add a dependency on `sebastian/resource-operations` to your project's `composer.json` file. Here is a minimal example of a `composer.json` file that just defines a dependency on this component:
```JSON
{
"require": {
"sebastian/resource-operations": "~1.0"
}
}
```
<?xml version="1.0" encoding="UTF-8"?>
<project name="resource-operations" default="build">
<target name="build" depends="generate" />
<target name="generate" depends="download-arginfo">
<exec executable="${basedir}/build/generate.php" taskname="generate" />
</target>
<target name="download-arginfo">
<tstamp>
<format property="thirty.days.ago" pattern="MM/dd/yyyy hh:mm aa" offset="-30" unit="day"/>
</tstamp>
<delete>
<fileset dir="${basedir}/build">
<include name="arginfo.php" />
<date datetime="${thirty.days.ago}" when="before"/>
</fileset>
</delete>
<get src="https://raw.githubusercontent.com/rlerdorf/phan/master/includes/arginfo.php" dest="${basedir}/build/arginfo.php" skipexisting="true"/>
</target>
</project>
#!/usr/bin/env php
<?php
/*
* This file is part of resource-operations.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
$functions = require __DIR__ . '/arginfo.php';
$resourceFunctions = [];
foreach ($functions as $function => $arguments) {
foreach ($arguments as $argument) {
if ($argument == 'resource') {
$resourceFunctions[] = $function;
}
}
}
$resourceFunctions = array_unique($resourceFunctions);
sort($resourceFunctions);
$buffer = <<<EOT
<?php
/*
* This file is part of resource-operations.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\ResourceOperations;
class ResourceOperations
{
/**
* @return string[]
*/
public static function getFunctions()
{
return [
EOT;
foreach ($resourceFunctions as $function) {
$buffer .= sprintf(" '%s',\n", $function);
}
$buffer .= <<< EOT
];
}
}
EOT;
file_put_contents(__DIR__ . '/../src/ResourceOperations.php', $buffer);
{
"name": "sebastian/resource-operations",
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"require": {
"php": ">=5.6.0"
},
"autoload": {
"classmap": [
"src/"
]
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
}
}
<?php
$finder = Symfony\CS\Finder\DefaultFinder::create()
->files()
->in('src')
->name('*.php');
return Symfony\CS\Config\Config::create()
->level(\Symfony\CS\FixerInterface::NONE_LEVEL)
->fixers(
array(
'align_double_arrow',
'align_equals',
'braces',
'concat_with_spaces',
'duplicate_semicolon',
'elseif',
'empty_return',
'encoding',
'eof_ending',
'extra_empty_lines',
'function_call_space',
'function_declaration',
'indentation',
'join_function',
'line_after_namespace',
'linefeed',
'list_commas',
'lowercase_constants',
'lowercase_keywords',
'method_argument_space',
'multiple_use',
'namespace_no_leading_whitespace',
'no_blank_lines_after_class_opening',
'no_empty_lines_after_phpdocs',
'parenthesis',
'php_closing_tag',
'phpdoc_indent',
'phpdoc_no_access',
'phpdoc_no_empty_return',
'phpdoc_no_package',
'phpdoc_params',
'phpdoc_scalar',
'phpdoc_separation',
'phpdoc_to_comment',
'phpdoc_trim',
'phpdoc_types',
'phpdoc_var_without_name',
'remove_lines_between_uses',
'return',
'self_accessor',
'short_array_syntax',
'short_tag',
'single_line_after_imports',
'single_quote',
'spaces_before_semicolon',
'spaces_cast',
'ternary_spaces',
'trailing_spaces',
'trim_array_spaces',
'unused_use',
'visibility',
'whitespacy_lines'
)
)
->finder($finder);
Version
Copyright (c) 2013-2015, Sebastian Bergmann <sebastian@phpunit.de>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Sebastian Bergmann nor the names of his
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
# Version
**Version** is a library that helps with managing the version number of Git-hosted PHP projects.
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
composer require sebastian/version
If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency:
composer require --dev sebastian/version
## Usage
The constructor of the `SebastianBergmann\Version` class expects two parameters:
* `$release` is the version number of the latest release (`X.Y.Z`, for instance) or the name of the release series (`X.Y`) when no release has been made from that branch / for that release series yet.
* `$path` is the path to the directory (or a subdirectory thereof) where the sourcecode of the project can be found. Simply passing `__DIR__` here usually suffices.
Apart from the constructor, the `SebastianBergmann\Version` class has a single public method: `getVersion()`.
Here is a contrived example that shows the basic usage:
<?php
$version = new SebastianBergmann\Version(
'3.7.10', '/usr/local/src/phpunit'
);
var_dump($version->getVersion());
?>
string(18) "3.7.10-17-g00f3408"
When a new release is prepared, the string that is passed to the constructor as the first argument needs to be updated.
### How SebastianBergmann\Version::getVersion() works
* If `$path` is not (part of) a Git repository and `$release` is in `X.Y.Z` format then `$release` is returned as-is.
* If `$path` is not (part of) a Git repository and `$release` is in `X.Y` format then `$release` is returned suffixed with `-dev`.
* If `$path` is (part of) a Git repository and `$release` is in `X.Y.Z` format then the output of `git describe --tags` is returned as-is.
* If `$path` is (part of) a Git repository and `$release` is in `X.Y` format then a string is returned that begins with `X.Y` and ends with information from `git describe --tags`.
{
"name": "sebastian/version",
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"support": {
"issues": "https://github.com/sebastianbergmann/version/issues"
},
"require": {
"php": ">=5.6"
},
"autoload": {
"classmap": [
"src/"
]
},
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
}
}
<?php
/*
* This file is part of the Version package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann;
/**
* @since Class available since Release 1.0.0
*/
class Version
{
/**
* @var string
*/
private $path;
/**
* @var string
*/
private $release;
/**
* @var string
*/
private $version;
/**
* @param string $release
* @param string $path
*/
public function __construct($release, $path)
{
$this->release = $release;
$this->path = $path;
}
/**
* @return string
*/
public function getVersion()
{
if ($this->version === null) {
if (count(explode('.', $this->release)) == 3) {
$this->version = $this->release;
} else {
$this->version = $this->release . '-dev';
}
$git = $this->getGitInformation($this->path);
if ($git) {
if (count(explode('.', $this->release)) == 3) {
$this->version = $git;
} else {
$git = explode('-', $git);
$this->version = $this->release . '-' . end($git);
}
}
}
return $this->version;
}
/**
* @param string $path
*
* @return bool|string
*/
private function getGitInformation($path)
{
if (!is_dir($path . DIRECTORY_SEPARATOR . '.git')) {
return false;
}
$process = proc_open(
'git describe --tags',
[
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
],
$pipes,
$path
);
if (!is_resource($process)) {
return false;
}
$result = trim(stream_get_contents($pipes[1]));
fclose($pipes[1]);
fclose($pipes[2]);
$returnCode = proc_close($process);
if ($returnCode !== 0) {
return false;
}
return $result;
}
}
*.crt -crlf
*.key -crlf
*.srl -crlf
*.pub -crlf
*.priv -crlf
*.txt -crlf
# ignore /notes in the git-generated distributed .zip archive
/notes export-ignore
/.php_cs.cache
/.phpunit
/build/*
/composer.lock
/phpunit.xml
/tests/acceptance.conf.php
/tests/smoke.conf.php
/vendor/
<?php
return PhpCsFixer\Config::create()
->setRules(array(
'@Symfony' => true,
'@Symfony:risky' => true,
'array_syntax' => array('syntax' => 'long'),
'no_unreachable_default_argument_value' => false,
'braces' => array('allow_single_line_closure' => true),
'heredoc_to_nowdoc' => false,
'phpdoc_annotation_without_dot' => false,
))
->setRiskyAllowed(true)
->setFinder(PhpCsFixer\Finder::create()->in(__DIR__))
;
language: php
sudo: false
before_script:
- cp tests/acceptance.conf.php.default tests/acceptance.conf.php
- cp tests/smoke.conf.php.default tests/smoke.conf.php
- composer self-update
- composer update --no-interaction --prefer-source
- gem install mime-types -v 2.99.1
- gem install mailcatcher
- mailcatcher --smtp-port 4456
script:
- phpunit --verbose
matrix:
include:
- php: 5.3
- php: 5.4
- php: 5.5
- php: 5.6
- php: 7.0
- php: hhvm
allow_failures:
- php: hhvm
fast_finish: true
Changelog
=========
5.4.6 (2017-02-13)
------------------
* removed exceptions thrown in destructors as they lead to fatal errors
* switched to use sha256 by default in DKIM as per the RFC
* fixed an 'Undefined variable: pipes' PHP notice
* fixed long To headers when using the mail transport
* fixed NTLMAuthenticator when no domain is passed with the username
* prevented fatal error during unserialization of a message
* fixed a PHP warning when sending a message that has a length of a multiple of 8192
5.4.5 (2016-12-29)
------------------
* SECURITY FIX: fixed CVE-2016-10074 by disallowing potentially unsafe shell characters
Prior to 5.4.5, the mail transport (Swift_Transport_MailTransport) was vulnerable to passing
arbitrary shell arguments if the "From", "ReturnPath" or "Sender" header came
from a non-trusted source, potentially allowing Remote Code Execution
* deprecated the mail transport
5.4.4 (2016-11-23)
------------------
* reverted escaping command-line args to mail (PHP mail() function already does it)
5.4.3 (2016-07-08)
------------------
* fixed SimpleHeaderSet::has()/get() when the 0 index is removed
* removed the need to have mcrypt installed
* fixed broken MIME header encoding with quotes/colons and non-ascii chars
* allowed mail transport send for messages without To header
* fixed PHP 7 support
5.4.2 (2016-05-01)
------------------
* fixed support for IPv6 sockets
* added auto-retry when sending messages from the memory spool
* fixed consecutive read calls in Swift_ByteStream_FileByteStream
* added support for iso-8859-15 encoding
* fixed PHP mail extra params on missing reversePath
* added methods to set custom stream context options
* fixed charset changes in QpContentEncoderProxy
* added return-path header to the ignoredHeaders list of DKIMSigner
* fixed crlf for subject using mail
* fixed add soft line break only when necessary
* fixed escaping command-line args to mail
5.4.1 (2015-06-06)
------------------
* made Swiftmailer exceptions confirm to PHP base exception constructor signature
* fixed MAIL FROM & RCPT TO headers to be RFC compliant
5.4.0 (2015-03-14)
------------------
* added the possibility to add extra certs to PKCS#7 signature
* fix base64 encoding with streams
* added a new RESULT_SPOOLED status for SpoolTransport
* fixed getBody() on attachments when called more than once
* removed dots from generated filenames in filespool
5.3.1 (2014-12-05)
------------------
* fixed cloning of messages with attachments
5.3.0 (2014-10-04)
------------------
* fixed cloning when using signers
* reverted removal of Swift_Encoding
* drop support for PHP 5.2.x
5.2.2 (2014-09-20)
------------------
* fixed Japanese support
* fixed the memory spool when the message changes when in the pool
* added support for cloning messages
* fixed PHP warning in the redirect plugin
* changed the way to and cc-ed email are sent to only use one transaction
5.2.1 (2014-06-13)
------------------
* SECURITY FIX: fixed CLI escaping when using sendmail as a transport
Prior to 5.2.1, the sendmail transport (Swift_Transport_SendmailTransport)
was vulnerable to an arbitrary shell execution if the "From" header came
from a non-trusted source and no "Return-Path" is configured.
* fixed parameter in DKIMSigner
* fixed compatibility with PHP < 5.4
5.2.0 (2014-05-08)
------------------
* fixed Swift_ByteStream_FileByteStream::read() to match to the specification
* fixed from-charset and to-charset arguments in mbstring_convert_encoding() usages
* fixed infinite loop in StreamBuffer
* fixed NullTransport to return the number of ignored emails instead of 0
* Use phpunit and mockery for unit testing (realityking)
5.1.0 (2014-03-18)
------------------
* fixed data writing to stream when sending large messages
* added support for libopendkim (https://github.com/xdecock/php-opendkim)
* merged SignedMessage and Message
* added Gmail XOAuth2 authentication
* updated the list of known mime types
* added NTLM authentication
5.0.3 (2013-12-03)
------------------
* fixed double-dot bug
* fixed DKIM signer
5.0.2 (2013-08-30)
------------------
* handled correct exception type while reading IoBuffer output
5.0.1 (2013-06-17)
------------------
* changed the spool to only start the transport when a mail has to be sent
* fixed compatibility with PHP 5.2
* fixed LICENSE file
5.0.0 (2013-04-30)
------------------
* changed the license from LGPL to MIT
4.3.1 (2013-04-11)
------------------
* removed usage of the native QP encoder when the charset is not UTF-8
* fixed usage of uniqid to avoid collisions
* made a performance improvement when tokenizing large headers
* fixed usage of the PHP native QP encoder on PHP 5.4.7+
4.3.0 (2013-01-08)
------------------
* made the temporary directory configurable via the TMPDIR env variable
* added S/MIME signer and encryption support
4.2.2 (2012-10-25)
------------------
* added the possibility to throttle messages per second in ThrottlerPlugin (mostly for Amazon SES)
* switched mime.qpcontentencoder to automatically use the PHP native encoder on PHP 5.4.7+
* allowed specifying a whitelist with regular expressions in RedirectingPlugin
4.2.1 (2012-07-13)
------------------
* changed the coding standards to PSR-1/2
* fixed issue with autoloading
* added NativeQpContentEncoder to enhance performance (for PHP 5.3+)
4.2.0 (2012-06-29)
------------------
* added documentation about how to use the Japanese support introduced in 4.1.8
* added a way to override the default configuration in a lazy way
* changed the PEAR init script to lazy-load the initialization
* fixed a bug when calling Swift_Preferences before anything else (regression introduced in 4.1.8)
4.1.8 (2012-06-17)
------------------
* added Japanese iso-2022-jp support
* changed the init script to lazy-load the initialization
* fixed docblocks (@id) which caused some problems with libraries parsing the dobclocks
* fixed Swift_Mime_Headers_IdentificationHeader::setId() when passed an array of ids
* fixed encoding of email addresses in headers
* added replacements setter to the Decorator plugin
4.1.7 (2012-04-26)
------------------
* fixed QpEncoder safeMapShareId property
4.1.6 (2012-03-23)
------------------
* reduced the size of serialized Messages
4.1.5 (2012-01-04)
------------------
* enforced Swift_Spool::queueMessage() to return a Boolean
* made an optimization to the memory spool: start the transport only when required
* prevented stream_socket_client() from generating an error and throw a Swift_TransportException instead
* fixed a PHP warning when calling to mail() when safe_mode is off
* many doc tweaks
4.1.4 (2011-12-16)
------------------
* added a memory spool (Swift_MemorySpool)
* fixed too many opened files when sending emails with attachments
4.1.3 (2011-10-27)
------------------
* added STARTTLS support
* added missing @return tags on fluent methods
* added a MessageLogger plugin that logs all sent messages
* added composer.json
4.1.2 (2011-09-13)
------------------
* fixed wrong detection of magic_quotes_runtime
* fixed fatal errors when no To or Subject header has been set
* fixed charset on parameter header continuations
* added documentation about how to install Swiftmailer from the PEAR channel
* fixed various typos and markup problem in the documentation
* fixed warning when cache directory does not exist
* fixed "slashes are escaped" bug
* changed require_once() to require() in autoload
4.1.1 (2011-07-04)
------------------
* added missing file in PEAR package
4.1.0 (2011-06-30)
------------------
* documentation has been converted to ReST
4.1.0 RC1 (2011-06-17)
----------------------
New features:
* changed the Decorator Plugin to allow replacements in all headers
* added Swift_Mime_Grammar and Swift_Validate to validate an email address
* modified the autoloader to lazy-initialize Swiftmailer
* removed Swift_Mailer::batchSend()
* added NullTransport
* added new plugins: RedirectingPlugin and ImpersonatePlugin
* added a way to send messages asynchronously (Spool)
Copyright (c) 2013-2016 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Swift Mailer
------------
Swift Mailer is a component based mailing solution for PHP 5.
It is released under the MIT license.
Homepage: http://swiftmailer.org
Documentation: http://swiftmailer.org/docs
Bugs: https://github.com/swiftmailer/swiftmailer/issues
Repository: https://github.com/swiftmailer/swiftmailer
Swift Mailer is highly object-oriented by design and lends itself
to use in complex web application with a great deal of flexibility.
For full details on usage, see the documentation.
{
"name": "swiftmailer/swiftmailer",
"type": "library",
"description": "Swiftmailer, free feature-rich PHP mailer",
"keywords": ["mail","mailer","email"],
"homepage": "http://swiftmailer.org",
"license": "MIT",
"authors": [
{
"name": "Chris Corbyn"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"mockery/mockery": "~0.9.1",
"symfony/phpunit-bridge": "~3.2"
},
"autoload": {
"files": ["lib/swift_required.php"]
},
"autoload-dev": {
"psr-0": {
"Swift_": "tests/unit"
}
},
"extra": {
"branch-alias": {
"dev-master": "5.4-dev"
}
}
}
Getting Help
============
There are a number of ways you can get help when using Swift Mailer, depending
upon the nature of your problem. For bug reports and feature requests create a
new ticket in GitHub. For general advice ask on the Google Group
(swiftmailer).
Submitting Bugs & Feature Requests
----------------------------------
Bugs and feature requests should be posted on GitHub.
If you post a bug or request a feature in the forum, or on the Google Group
you will most likely be asked to create a ticket in `GitHub`_ since it is
simply not feasible to manage such requests from a number of a different
sources.
When you go to GitHub you will be asked to create a username and password
before you can create a ticket. This is free and takes very little time.
When you create your ticket, do not assign it to any milestones. A developer
will assess your ticket and re-assign it as needed.
If your ticket is reporting a bug present in the current version, which was
not present in the previous version please include the tag "regression" in
your ticket.
GitHub will update you when work is performed on your ticket.
Ask on the Google Group
-----------------------
You can seek advice at Google Groups, within the "swiftmailer" `group`_.
You can post messages to this group if you want help, or there's something you
wish to discuss with the developers and with other users.
This is probably the fastest way to get help since it is primarily email-based
for most users, though bug reports should not be posted here since they may
not be resolved.
.. _`GitHub`: https://github.com/swiftmailer/swiftmailer/issues
.. _`group`: http://groups.google.com/group/swiftmailer
Including Swift Mailer (Autoloading)
====================================
If you are using Composer, Swift Mailer will be automatically autoloaded.
If not, you can use the built-in autoloader by requiring the
``swift_required.php`` file::
require_once '/path/to/swift-mailer/lib/swift_required.php';
/* rest of code goes here */
If you want to override the default Swift Mailer configuration, call the
``init()`` method on the ``Swift`` class and pass it a valid PHP callable (a
PHP function name, a PHP 5.3 anonymous function, ...)::
require_once '/path/to/swift-mailer/lib/swift_required.php';
function swiftmailer_configurator() {
// configure Swift Mailer
Swift_DependencyContainer::getInstance()->...
Swift_Preferences::getInstance()->...
}
Swift::init('swiftmailer_configurator');
/* rest of code goes here */
The advantage of using the ``init()`` method is that your code will be
executed only if you use Swift Mailer in your script.
.. note::
While Swift Mailer's autoloader is designed to play nicely with other
autoloaders, sometimes you may have a need to avoid using Swift Mailer's
autoloader and use your own instead. Include the ``swift_init.php``
instead of the ``swift_required.php`` if you need to do this. The very
minimum include is the ``swift_init.php`` file since Swift Mailer will not
work without the dependency injection this file sets up:
.. code-block:: php
require_once '/path/to/swift-mailer/lib/swift_init.php';
/* rest of code goes here */
Swiftmailer
===========
.. toctree::
:maxdepth: 2
introduction
overview
installing
help-resources
including-the-files
messages
headers
sending
plugins
japanese
Installing the Library
======================
Installing with Composer
------------------------
The recommended way to install Swiftmailer is via Composer:
.. code-block:: bash
$ php composer.phar require swiftmailer/swiftmailer @stable
Installing from Git
-------------------
It's possible to download and install Swift Mailer directly from github.com if
you want to keep up-to-date with ease.
Swift Mailer's source code is kept in a git repository at github.com so you
can get the source directly from the repository.
.. note::
You do not need to have git installed to use Swift Mailer from GitHub. If
you don't have git installed, go to `GitHub`_ and click the "Download"
button.
Cloning the Repository
~~~~~~~~~~~~~~~~~~~~~~
The repository can be cloned from git://github.com/swiftmailer/swiftmailer.git
using the ``git clone`` command.
You will need to have ``git`` installed before you can use the
``git clone`` command.
To clone the repository:
* Open your favorite terminal environment (command line).
* Move to the directory you want to clone to.
* Run the command ``git clone git://github.com/swiftmailer/swiftmailer.git
swiftmailer``.
The source code will be downloaded into a directory called "swiftmailer".
The example shows the process on a UNIX-like system such as Linux, BSD or Mac
OS X.
.. code-block:: bash
$ cd source_code/
$ git clone git://github.com/swiftmailer/swiftmailer.git swiftmailer
Initialized empty Git repository in /Users/chris/source_code/swiftmailer/.git/
remote: Counting objects: 6815, done.
remote: Compressing objects: 100% (2761/2761), done.
remote: Total 6815 (delta 3641), reused 6326 (delta 3286)
Receiving objects: 100% (6815/6815), 4.35 MiB | 162 KiB/s, done.
Resolving deltas: 100% (3641/3641), done.
Checking out files: 100% (1847/1847), done.
$ cd swiftmailer/
$ ls
CHANGES LICENSE ...
$
Troubleshooting
---------------
Swift Mailer does not work when used with function overloading as implemented
by ``mbstring`` (``mbstring.func_overload`` set to ``2``). A workaround is to
temporarily change the internal encoding to ``ASCII`` when sending an email:
.. code-block:: php
if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2)
{
$mbEncoding = mb_internal_encoding();
mb_internal_encoding('ASCII');
}
// Create your message and send it with Swift Mailer
if (isset($mbEncoding))
{
mb_internal_encoding($mbEncoding);
}
.. _`GitHub`: http://github.com/swiftmailer/swiftmailer
Introduction
============
Swift Mailer is a component-based library for sending e-mails from PHP
applications.
Organization of this Book
-------------------------
This book has been written so that those who need information quickly are able
to find what they need, and those who wish to learn more advanced topics can
read deeper into each chapter.
The book begins with an overview of Swift Mailer, discussing what's included
in the package and preparing you for the remainder of the book.
It is possible to read this user guide just like any other book (from
beginning to end). Each chapter begins with a discussion of the contents it
contains, followed by a short code sample designed to give you a head start.
As you get further into a chapter you will learn more about Swift Mailer's
capabilities, but often you will be able to head directly to the topic you
wish to learn about.
Throughout this book you will be presented with code samples, which most
people should find ample to implement Swift Mailer appropriately in their own
projects. We will also use diagrams where appropriate, and where we believe
readers may find it helpful we will discuss some related theory, including
reference to certain documents you are able to find online.
Code Samples
------------
Code samples presented in this book will be displayed on a different colored
background in a monospaced font. Samples are not to be taken as copy & paste
code snippets.
Code examples are used through the book to clarify what is written in text.
They will sometimes be usable as-is, but they should always be taken as
outline/pseudo code only.
A code sample will look like this::
class AClass
{
...
}
// A Comment
$obj = new AClass($arg1, $arg2, ... );
/* A note about another way of doing something
$obj = AClass::newInstance($arg1, $arg2, ... );
*/
The presence of 3 dots ``...`` in a code sample indicates that we have left
out a chunk of the code for brevity, they are not actually part of the code.
We will often place multi-line comments ``/* ... */`` in the code so that we
can show alternative ways of achieving the same result.
You should read the code examples given and try to understand them. They are
kept concise so that you are not overwhelmed with information.
History of Swift Mailer
-----------------------
Swift Mailer began back in 2005 as a one-class project for sending mail over
SMTP. It has since grown into the flexible component-based library that is in
development today.
Chris Corbyn first posted Swift Mailer on a web forum asking for comments from
other developers. It was never intended as a fully supported open source
project, but members of the forum began to adopt it and make use of it.
Very quickly feature requests were coming for the ability to add attachments
and use SMTP authentication, along with a number of other "obvious" missing
features. Considering the only alternative was PHPMailer it seemed like a good
time to bring some fresh tools to the table. Chris began working towards a
more component based, PHP5-like approach unlike the existing single-class,
legacy PHP4 approach taken by PHPMailer.
Members of the forum offered a lot of advice and critique on the code as he
worked through this project and released versions 2 and 3 of the library in
2005 and 2006, which by then had been broken down into smaller classes
offering more flexibility and supporting plugins. To this day the Swift Mailer
team still receive a lot of feature requests from users both on the forum and
in by email.
Until 2008 Chris was the sole developer of Swift Mailer, but entering 2009 he
gained the support of two experienced developers well-known to him: Paul
Annesley and Christopher Thompson. This has been an extremely welcome change.
As of September 2009, Chris handed over the maintenance of Swift Mailer to
Fabien Potencier.
Now 2009 and in its fourth major version Swift Mailer is more object-oriented
and flexible than ever, both from a usability standpoint and from a
development standpoint.
By no means is Swift Mailer ready to call "finished". There are still many
features that can be added to the library along with the constant refactoring
that happens behind the scenes.
It's a Library!
---------------
Swift Mailer is not an application - it's a library.
To most experienced developers this is probably an obvious point to make, but
it's certainly worth mentioning. Many people often contact us having gotten
the completely wrong end of the stick in terms of what Swift Mailer is
actually for.
It's not an application. It does not have a graphical user interface. It
cannot be opened in your web browser directly.
It's a library (or a framework if you like). It provides a whole lot of
classes that do some very complicated things, so that you don't have to. You
"use" Swift Mailer within an application so that your application can have the
ability to send emails.
The component-based structure of the library means that you are free to
implement it in a number of different ways and that you can pick and choose
what you want to use.
An application on the other hand (such as a blog or a forum) is already "put
together" in a particular way, (usually) provides a graphical user interface
and most likely doesn't offer a great deal of integration with your own
application.
Embrace the structure of the library and use the components it offers to your
advantage. Learning what the components do, rather than blindly copying and
pasting existing code will put you in a great position to build a powerful
application!
Using Swift Mailer for Japanese Emails
======================================
To send emails in Japanese, you need to tweak the default configuration.
After requiring the Swift Mailer autoloader (by including the
``swift_required.php`` file), call the ``Swift::init()`` method with the
following code::
require_once '/path/to/swift-mailer/lib/swift_required.php';
Swift::init(function () {
Swift_DependencyContainer::getInstance()
->register('mime.qpheaderencoder')
->asAliasOf('mime.base64headerencoder');
Swift_Preferences::getInstance()->setCharset('iso-2022-jp');
});
/* rest of code goes here */
That's all!
Library Overview
================
Most features (and more) of your every day mail client software are provided
by Swift Mailer, using object-oriented PHP code as the interface.
In this chapter we will take a short tour of the various components, which put
together form the Swift Mailer library as a whole. You will learn key
terminology used throughout the rest of this book and you will gain a little
understanding of the classes you will work with as you integrate Swift Mailer
into your application.
This chapter is intended to prepare you for the information contained in the
subsequent chapters of this book. You may choose to skip this chapter if you
are fairly technically minded, though it is likely to save you some time in
the long run if you at least read between the lines here.
System Requirements
-------------------
The basic requirements to operate Swift Mailer are extremely minimal and
easily achieved. Historically, Swift Mailer has supported both PHP 4 and PHP 5
by following a parallel development workflow. Now in it's fourth major
version, and Swift Mailer operates on servers running PHP 5.3.3 or higher.
The library aims to work with as many PHP 5 projects as possible:
* PHP 5.3.3 or higher, with the SPL extension (standard)
* Limited network access to connect to remote SMTP servers
* 8 MB or more memory limit (Swift Mailer uses around 2 MB)
Component Breakdown
-------------------
Swift Mailer is made up of many classes. Each of these classes can be grouped
into a general "component" group which describes the task it is designed to
perform.
We'll take a brief look at the components which form Swift Mailer in this
section of the book.
The Mailer
~~~~~~~~~~
The mailer class, ``Swift_Mailer`` is the central class in the library where
all of the other components meet one another. ``Swift_Mailer`` acts as a sort
of message dispatcher, communicating with the underlying Transport to deliver
your Message to all intended recipients.
If you were to dig around in the source code for Swift Mailer you'd notice
that ``Swift_Mailer`` itself is pretty bare. It delegates to other objects for
most tasks and in theory, if you knew the internals of Swift Mailer well you
could by-pass this class entirely. We wouldn't advise doing such a thing
however -- there are reasons this class exists:
* for consistency, regardless of the Transport used
* to provide abstraction from the internals in the event internal API changes
are made
* to provide convenience wrappers around aspects of the internal API
An instance of ``Swift_Mailer`` is created by the developer before sending any
Messages.
Transports
~~~~~~~~~~
Transports are the classes in Swift Mailer that are responsible for
communicating with a service in order to deliver a Message. There are several
types of Transport in Swift Mailer, all of which implement the Swift_Transport
interface and offer underlying start(), stop() and send() methods.
Typically you will not need to know how a Transport works under-the-surface,
you will only need to know how to create an instance of one, and which one to
use for your environment.
+---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| Class | Features | Pros/cons |
+=================================+=============================================================================================+===============================================================================================================================================+
| ``Swift_SmtpTransport`` | Sends messages over SMTP; Supports Authentication; Supports Encryption | Very portable; Pleasingly predictable results; Provides good feedback |
+---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| ``Swift_SendmailTransport`` | Communicates with a locally installed ``sendmail`` executable (Linux/UNIX) | Quick time-to-run; Provides less-accurate feedback than SMTP; Requires ``sendmail`` installation |
+---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| ``Swift_LoadBalancedTransport`` | Cycles through a collection of the other Transports to manage load-reduction | Provides graceful fallback if one Transport fails (e.g. an SMTP server is down); Keeps the load on remote services down by spreading the work |
+---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| ``Swift_FailoverTransport`` | Works in conjunction with a collection of the other Transports to provide high-availability | Provides graceful fallback if one Transport fails (e.g. an SMTP server is down) |
+---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
MIME Entities
~~~~~~~~~~~~~
Everything that forms part of a Message is called a MIME Entity. All MIME
entities in Swift Mailer share a common set of features. There are various
types of MIME entity that serve different purposes such as Attachments and
MIME parts.
An e-mail message is made up of several relatively simple entities that are
combined in different ways to achieve different results. All of these entities
have the same fundamental outline but serve a different purpose. The Message
itself can be defined as a MIME entity, an Attachment is a MIME entity, all
MIME parts are MIME entities -- and so on!
The basic units of each MIME entity -- be it the Message itself, or an
Attachment -- are its Headers and its body:
.. code-block:: text
Other-Header: Another value
The body content itself
The Headers of a MIME entity, and its body must conform to some strict
standards defined by various RFC documents. Swift Mailer ensures that these
specifications are followed by using various types of object, including
Encoders and different Header types to generate the entity.
Each MIME component implements the base ``Swift_Mime_MimeEntity`` interface,
which offers methods for retrieving Headers, adding new Headers, changing the
Encoder, updating the body and so on!
All MIME entities have one Header in common -- the Content-Type Header,
updated with the entity's ``setContentType()`` method.
Encoders
~~~~~~~~
Encoders are used to transform the content of Messages generated in Swift
Mailer into a format that is safe to send across the internet and that
conforms to RFC specifications.
Generally speaking you will not need to interact with the Encoders in Swift
Mailer -- the correct settings will be handled by the library itself.
However they are probably worth a brief mention in the event that you do want
to play with them.
Both the Headers and the body of all MIME entities (including the Message
itself) use Encoders to ensure the data they contain can be sent over the
internet without becoming corrupted or misinterpreted.
There are two types of Encoder: Base64 and Quoted-Printable.
Plugins
~~~~~~~
Plugins exist to extend, or modify the behaviour of Swift Mailer. They respond
to Events that are fired within the Transports during sending.
There are a number of Plugins provided as part of the base Swift Mailer
package and they all follow a common interface to respond to Events fired
within the library. Interfaces are provided to "listen" to each type of Event
fired and to act as desired when a listened-to Event occurs.
Although several plugins are provided with Swift Mailer out-of-the-box, the
Events system has been specifically designed to make it easy for experienced
object-oriented developers to write their own plugins in order to achieve
goals that may not be possible with the base library.
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* General utility class in Swift Mailer, not to be instantiated.
*
*
* @author Chris Corbyn
*/
abstract class Swift
{
/** Swift Mailer Version number generated during dist release process */
const VERSION = '@SWIFT_VERSION_NUMBER@';
public static $initialized = false;
public static $inits = array();
/**
* Registers an initializer callable that will be called the first time
* a SwiftMailer class is autoloaded.
*
* This enables you to tweak the default configuration in a lazy way.
*
* @param mixed $callable A valid PHP callable that will be called when autoloading the first Swift class
*/
public static function init($callable)
{
self::$inits[] = $callable;
}
/**
* Internal autoloader for spl_autoload_register().
*
* @param string $class
*/
public static function autoload($class)
{
// Don't interfere with other autoloaders
if (0 !== strpos($class, 'Swift_')) {
return;
}
$path = __DIR__.'/'.str_replace('_', '/', $class).'.php';
if (!file_exists($path)) {
return;
}
require $path;
if (self::$inits && !self::$initialized) {
self::$initialized = true;
foreach (self::$inits as $init) {
call_user_func($init);
}
}
}
/**
* Configure autoloading using Swift Mailer.
*
* This is designed to play nicely with other autoloaders.
*
* @param mixed $callable A valid PHP callable that will be called when autoloading the first Swift class
*/
public static function registerAutoload($callable = null)
{
if (null !== $callable) {
self::$inits[] = $callable;
}
spl_autoload_register(array('Swift', 'autoload'));
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Attachment class for attaching files to a {@link Swift_Mime_Message}.
*
* @author Chris Corbyn
*/
class Swift_Attachment extends Swift_Mime_Attachment
{
/**
* Create a new Attachment.
*
* Details may be optionally provided to the constructor.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*/
public function __construct($data = null, $filename = null, $contentType = null)
{
call_user_func_array(
array($this, 'Swift_Mime_Attachment::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('mime.attachment')
);
$this->setBody($data);
$this->setFilename($filename);
if ($contentType) {
$this->setContentType($contentType);
}
}
/**
* Create a new Attachment.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*
* @return Swift_Mime_Attachment
*/
public static function newInstance($data = null, $filename = null, $contentType = null)
{
return new self($data, $filename, $contentType);
}
/**
* Create a new Attachment from a filesystem path.
*
* @param string $path
* @param string $contentType optional
*
* @return Swift_Mime_Attachment
*/
public static function fromPath($path, $contentType = null)
{
return self::newInstance()->setFile(
new Swift_ByteStream_FileByteStream($path),
$contentType
);
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides the base functionality for an InputStream supporting filters.
*
* @author Chris Corbyn
*/
abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_InputByteStream, Swift_Filterable
{
/**
* Write sequence.
*/
protected $_sequence = 0;
/**
* StreamFilters.
*
* @var Swift_StreamFilter[]
*/
private $_filters = array();
/**
* A buffer for writing.
*/
private $_writeBuffer = '';
/**
* Bound streams.
*
* @var Swift_InputByteStream[]
*/
private $_mirrors = array();
/**
* Commit the given bytes to the storage medium immediately.
*
* @param string $bytes
*/
abstract protected function _commit($bytes);
/**
* Flush any buffers/content with immediate effect.
*/
abstract protected function _flush();
/**
* Add a StreamFilter to this InputByteStream.
*
* @param Swift_StreamFilter $filter
* @param string $key
*/
public function addFilter(Swift_StreamFilter $filter, $key)
{
$this->_filters[$key] = $filter;
}
/**
* Remove an already present StreamFilter based on its $key.
*
* @param string $key
*/
public function removeFilter($key)
{
unset($this->_filters[$key]);
}
/**
* Writes $bytes to the end of the stream.
*
* @param string $bytes
*
* @throws Swift_IoException
*
* @return int
*/
public function write($bytes)
{
$this->_writeBuffer .= $bytes;
foreach ($this->_filters as $filter) {
if ($filter->shouldBuffer($this->_writeBuffer)) {
return;
}
}
$this->_doWrite($this->_writeBuffer);
return ++$this->_sequence;
}
/**
* For any bytes that are currently buffered inside the stream, force them
* off the buffer.
*
* @throws Swift_IoException
*/
public function commit()
{
$this->_doWrite($this->_writeBuffer);
}
/**
* Attach $is to this stream.
*
* The stream acts as an observer, receiving all data that is written.
* All {@link write()} and {@link flushBuffers()} operations will be mirrored.
*
* @param Swift_InputByteStream $is
*/
public function bind(Swift_InputByteStream $is)
{
$this->_mirrors[] = $is;
}
/**
* Remove an already bound stream.
*
* If $is is not bound, no errors will be raised.
* If the stream currently has any buffered data it will be written to $is
* before unbinding occurs.
*
* @param Swift_InputByteStream $is
*/
public function unbind(Swift_InputByteStream $is)
{
foreach ($this->_mirrors as $k => $stream) {
if ($is === $stream) {
if ($this->_writeBuffer !== '') {
$stream->write($this->_writeBuffer);
}
unset($this->_mirrors[$k]);
}
}
}
/**
* Flush the contents of the stream (empty it) and set the internal pointer
* to the beginning.
*
* @throws Swift_IoException
*/
public function flushBuffers()
{
if ($this->_writeBuffer !== '') {
$this->_doWrite($this->_writeBuffer);
}
$this->_flush();
foreach ($this->_mirrors as $stream) {
$stream->flushBuffers();
}
}
/** Run $bytes through all filters */
private function _filter($bytes)
{
foreach ($this->_filters as $filter) {
$bytes = $filter->filter($bytes);
}
return $bytes;
}
/** Just write the bytes to the stream */
private function _doWrite($bytes)
{
$this->_commit($this->_filter($bytes));
foreach ($this->_mirrors as $stream) {
$stream->write($bytes);
}
$this->_writeBuffer = '';
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Allows reading and writing of bytes to and from an array.
*
* @author Chris Corbyn
*/
class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_OutputByteStream
{
/**
* The internal stack of bytes.
*
* @var string[]
*/
private $_array = array();
/**
* The size of the stack.
*
* @var int
*/
private $_arraySize = 0;
/**
* The internal pointer offset.
*
* @var int
*/
private $_offset = 0;
/**
* Bound streams.
*
* @var Swift_InputByteStream[]
*/
private $_mirrors = array();
/**
* Create a new ArrayByteStream.
*
* If $stack is given the stream will be populated with the bytes it contains.
*
* @param mixed $stack of bytes in string or array form, optional
*/
public function __construct($stack = null)
{
if (is_array($stack)) {
$this->_array = $stack;
$this->_arraySize = count($stack);
} elseif (is_string($stack)) {
$this->write($stack);
} else {
$this->_array = array();
}
}
/**
* Reads $length bytes from the stream into a string and moves the pointer
* through the stream by $length.
*
* If less bytes exist than are requested the
* remaining bytes are given instead. If no bytes are remaining at all, boolean
* false is returned.
*
* @param int $length
*
* @return string
*/
public function read($length)
{
if ($this->_offset == $this->_arraySize) {
return false;
}
// Don't use array slice
$end = $length + $this->_offset;
$end = $this->_arraySize < $end ? $this->_arraySize : $end;
$ret = '';
for (; $this->_offset < $end; ++$this->_offset) {
$ret .= $this->_array[$this->_offset];
}
return $ret;
}
/**
* Writes $bytes to the end of the stream.
*
* @param string $bytes
*/
public function write($bytes)
{
$to_add = str_split($bytes);
foreach ($to_add as $value) {
$this->_array[] = $value;
}
$this->_arraySize = count($this->_array);
foreach ($this->_mirrors as $stream) {
$stream->write($bytes);
}
}
/**
* Not used.
*/
public function commit()
{
}
/**
* Attach $is to this stream.
*
* The stream acts as an observer, receiving all data that is written.
* All {@link write()} and {@link flushBuffers()} operations will be mirrored.
*
* @param Swift_InputByteStream $is
*/
public function bind(Swift_InputByteStream $is)
{
$this->_mirrors[] = $is;
}
/**
* Remove an already bound stream.
*
* If $is is not bound, no errors will be raised.
* If the stream currently has any buffered data it will be written to $is
* before unbinding occurs.
*
* @param Swift_InputByteStream $is
*/
public function unbind(Swift_InputByteStream $is)
{
foreach ($this->_mirrors as $k => $stream) {
if ($is === $stream) {
unset($this->_mirrors[$k]);
}
}
}
/**
* Move the internal read pointer to $byteOffset in the stream.
*
* @param int $byteOffset
*
* @return bool
*/
public function setReadPointer($byteOffset)
{
if ($byteOffset > $this->_arraySize) {
$byteOffset = $this->_arraySize;
} elseif ($byteOffset < 0) {
$byteOffset = 0;
}
$this->_offset = $byteOffset;
}
/**
* Flush the contents of the stream (empty it) and set the internal pointer
* to the beginning.
*/
public function flushBuffers()
{
$this->_offset = 0;
$this->_array = array();
$this->_arraySize = 0;
foreach ($this->_mirrors as $stream) {
$stream->flushBuffers();
}
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Allows reading and writing of bytes to and from a file.
*
* @author Chris Corbyn
*/
class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream implements Swift_FileStream
{
/** The internal pointer offset */
private $_offset = 0;
/** The path to the file */
private $_path;
/** The mode this file is opened in for writing */
private $_mode;
/** A lazy-loaded resource handle for reading the file */
private $_reader;
/** A lazy-loaded resource handle for writing the file */
private $_writer;
/** If magic_quotes_runtime is on, this will be true */
private $_quotes = false;
/** If stream is seekable true/false, or null if not known */
private $_seekable = null;
/**
* Create a new FileByteStream for $path.
*
* @param string $path
* @param bool $writable if true
*/
public function __construct($path, $writable = false)
{
if (empty($path)) {
throw new Swift_IoException('The path cannot be empty');
}
$this->_path = $path;
$this->_mode = $writable ? 'w+b' : 'rb';
if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) {
$this->_quotes = true;
}
}
/**
* Get the complete path to the file.
*
* @return string
*/
public function getPath()
{
return $this->_path;
}
/**
* Reads $length bytes from the stream into a string and moves the pointer
* through the stream by $length.
*
* If less bytes exist than are requested the
* remaining bytes are given instead. If no bytes are remaining at all, boolean
* false is returned.
*
* @param int $length
*
* @throws Swift_IoException
*
* @return string|bool
*/
public function read($length)
{
$fp = $this->_getReadHandle();
if (!feof($fp)) {
if ($this->_quotes) {
ini_set('magic_quotes_runtime', 0);
}
$bytes = fread($fp, $length);
if ($this->_quotes) {
ini_set('magic_quotes_runtime', 1);
}
$this->_offset = ftell($fp);
// If we read one byte after reaching the end of the file
// feof() will return false and an empty string is returned
if ($bytes === '' && feof($fp)) {
$this->_resetReadHandle();
return false;
}
return $bytes;
}
$this->_resetReadHandle();
return false;
}
/**
* Move the internal read pointer to $byteOffset in the stream.
*
* @param int $byteOffset
*
* @return bool
*/
public function setReadPointer($byteOffset)
{
if (isset($this->_reader)) {
$this->_seekReadStreamToPosition($byteOffset);
}
$this->_offset = $byteOffset;
}
/** Just write the bytes to the file */
protected function _commit($bytes)
{
fwrite($this->_getWriteHandle(), $bytes);
$this->_resetReadHandle();
}
/** Not used */
protected function _flush()
{
}
/** Get the resource for reading */
private function _getReadHandle()
{
if (!isset($this->_reader)) {
$pointer = @fopen($this->_path, 'rb');
if (!$pointer) {
throw new Swift_IoException(
'Unable to open file for reading ['.$this->_path.']'
);
}
$this->_reader = $pointer;
if ($this->_offset != 0) {
$this->_getReadStreamSeekableStatus();
$this->_seekReadStreamToPosition($this->_offset);
}
}
return $this->_reader;
}
/** Get the resource for writing */
private function _getWriteHandle()
{
if (!isset($this->_writer)) {
if (!$this->_writer = fopen($this->_path, $this->_mode)) {
throw new Swift_IoException(
'Unable to open file for writing ['.$this->_path.']'
);
}
}
return $this->_writer;
}
/** Force a reload of the resource for reading */
private function _resetReadHandle()
{
if (isset($this->_reader)) {
fclose($this->_reader);
$this->_reader = null;
}
}
/** Check if ReadOnly Stream is seekable */
private function _getReadStreamSeekableStatus()
{
$metas = stream_get_meta_data($this->_reader);
$this->_seekable = $metas['seekable'];
}
/** Streams in a readOnly stream ensuring copy if needed */
private function _seekReadStreamToPosition($offset)
{
if ($this->_seekable === null) {
$this->_getReadStreamSeekableStatus();
}
if ($this->_seekable === false) {
$currentPos = ftell($this->_reader);
if ($currentPos < $offset) {
$toDiscard = $offset - $currentPos;
fread($this->_reader, $toDiscard);
return;
}
$this->_copyReadStream();
}
fseek($this->_reader, $offset, SEEK_SET);
}
/** Copy a readOnly Stream to ensure seekability */
private function _copyReadStream()
{
if ($tmpFile = fopen('php://temp/maxmemory:4096', 'w+b')) {
/* We have opened a php:// Stream Should work without problem */
} elseif (function_exists('sys_get_temp_dir') && is_writable(sys_get_temp_dir()) && ($tmpFile = tmpfile())) {
/* We have opened a tmpfile */
} else {
throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available');
}
$currentPos = ftell($this->_reader);
fclose($this->_reader);
$source = fopen($this->_path, 'rb');
if (!$source) {
throw new Swift_IoException('Unable to open file for copying ['.$this->_path.']');
}
fseek($tmpFile, 0, SEEK_SET);
while (!feof($source)) {
fwrite($tmpFile, fread($source, 4096));
}
fseek($tmpFile, $currentPos, SEEK_SET);
fclose($source);
$this->_reader = $tmpFile;
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* @author Romain-Geissler
*/
class Swift_ByteStream_TemporaryFileByteStream extends Swift_ByteStream_FileByteStream
{
public function __construct()
{
$filePath = tempnam(sys_get_temp_dir(), 'FileByteStream');
if ($filePath === false) {
throw new Swift_IoException('Failed to retrieve temporary file name.');
}
parent::__construct($filePath, true);
}
public function getContent()
{
if (($content = file_get_contents($this->getPath())) === false) {
throw new Swift_IoException('Failed to get temporary file content.');
}
return $content;
}
public function __destruct()
{
if (file_exists($this->getPath())) {
@unlink($this->getPath());
}
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Analyzes characters for a specific character set.
*
* @author Chris Corbyn
* @author Xavier De Cock <xdecock@gmail.com>
*/
interface Swift_CharacterReader
{
const MAP_TYPE_INVALID = 0x01;
const MAP_TYPE_FIXED_LEN = 0x02;
const MAP_TYPE_POSITIONS = 0x03;
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param mixed $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars);
/**
* Returns the mapType, see constants.
*
* @return int
*/
public function getMapType();
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
*
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param int[] $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size);
/**
* Returns the number of bytes which should be read to start each character.
*
* For fixed width character sets this should be the number of octets-per-character.
* For multibyte character sets this will probably be 1.
*
* @return int
*/
public function getInitialByteSize();
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides fixed-width byte sizes for reading fixed-width character sets.
*
* @author Chris Corbyn
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterReader
{
/**
* The number of bytes in a single character.
*
* @var int
*/
private $_width;
/**
* Creates a new GenericFixedWidthReader using $width bytes per character.
*
* @param int $width
*/
public function __construct($width)
{
$this->_width = $width;
}
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param mixed $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars)
{
$strlen = strlen($string);
// % and / are CPU intensive, so, maybe find a better way
$ignored = $strlen % $this->_width;
$ignoredChars = $ignored ? substr($string, -$ignored) : '';
$currentMap = $this->_width;
return ($strlen - $ignored) / $this->_width;
}
/**
* Returns the mapType.
*
* @return int
*/
public function getMapType()
{
return self::MAP_TYPE_FIXED_LEN;
}
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
*
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param string $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size)
{
$needed = $this->_width - $size;
return $needed > -1 ? $needed : -1;
}
/**
* Returns the number of bytes which should be read to start each character.
*
* @return int
*/
public function getInitialByteSize()
{
return $this->_width;
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Analyzes US-ASCII characters.
*
* @author Chris Corbyn
*/
class Swift_CharacterReader_UsAsciiReader implements Swift_CharacterReader
{
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param string $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars)
{
$strlen = strlen($string);
$ignoredChars = '';
for ($i = 0; $i < $strlen; ++$i) {
if ($string[$i] > "\x07F") {
// Invalid char
$currentMap[$i + $startOffset] = $string[$i];
}
}
return $strlen;
}
/**
* Returns mapType.
*
* @return int mapType
*/
public function getMapType()
{
return self::MAP_TYPE_INVALID;
}
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param string $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size)
{
$byte = reset($bytes);
if (1 == count($bytes) && $byte >= 0x00 && $byte <= 0x7F) {
return 0;
}
return -1;
}
/**
* Returns the number of bytes which should be read to start each character.
*
* @return int
*/
public function getInitialByteSize()
{
return 1;
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Analyzes UTF-8 characters.
*
* @author Chris Corbyn
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_CharacterReader_Utf8Reader implements Swift_CharacterReader
{
/** Pre-computed for optimization */
private static $length_map = array(
// N=0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x0N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x1N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x2N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x3N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x4N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x5N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x6N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x7N
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x8N
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x9N
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xAN
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xBN
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xCN
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xDN
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEN
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0, // 0xFN
);
private static $s_length_map = array(
"\x00" => 1, "\x01" => 1, "\x02" => 1, "\x03" => 1, "\x04" => 1, "\x05" => 1, "\x06" => 1, "\x07" => 1,
"\x08" => 1, "\x09" => 1, "\x0a" => 1, "\x0b" => 1, "\x0c" => 1, "\x0d" => 1, "\x0e" => 1, "\x0f" => 1,
"\x10" => 1, "\x11" => 1, "\x12" => 1, "\x13" => 1, "\x14" => 1, "\x15" => 1, "\x16" => 1, "\x17" => 1,
"\x18" => 1, "\x19" => 1, "\x1a" => 1, "\x1b" => 1, "\x1c" => 1, "\x1d" => 1, "\x1e" => 1, "\x1f" => 1,
"\x20" => 1, "\x21" => 1, "\x22" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1,
"\x28" => 1, "\x29" => 1, "\x2a" => 1, "\x2b" => 1, "\x2c" => 1, "\x2d" => 1, "\x2e" => 1, "\x2f" => 1,
"\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, "\x36" => 1, "\x37" => 1,
"\x38" => 1, "\x39" => 1, "\x3a" => 1, "\x3b" => 1, "\x3c" => 1, "\x3d" => 1, "\x3e" => 1, "\x3f" => 1,
"\x40" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, "\x45" => 1, "\x46" => 1, "\x47" => 1,
"\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, "\x4d" => 1, "\x4e" => 1, "\x4f" => 1,
"\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, "\x55" => 1, "\x56" => 1, "\x57" => 1,
"\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5b" => 1, "\x5c" => 1, "\x5d" => 1, "\x5e" => 1, "\x5f" => 1,
"\x60" => 1, "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1,
"\x68" => 1, "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1,
"\x70" => 1, "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1,
"\x78" => 1, "\x79" => 1, "\x7a" => 1, "\x7b" => 1, "\x7c" => 1, "\x7d" => 1, "\x7e" => 1, "\x7f" => 1,
"\x80" => 0, "\x81" => 0, "\x82" => 0, "\x83" => 0, "\x84" => 0, "\x85" => 0, "\x86" => 0, "\x87" => 0,
"\x88" => 0, "\x89" => 0, "\x8a" => 0, "\x8b" => 0, "\x8c" => 0, "\x8d" => 0, "\x8e" => 0, "\x8f" => 0,
"\x90" => 0, "\x91" => 0, "\x92" => 0, "\x93" => 0, "\x94" => 0, "\x95" => 0, "\x96" => 0, "\x97" => 0,
"\x98" => 0, "\x99" => 0, "\x9a" => 0, "\x9b" => 0, "\x9c" => 0, "\x9d" => 0, "\x9e" => 0, "\x9f" => 0,
"\xa0" => 0, "\xa1" => 0, "\xa2" => 0, "\xa3" => 0, "\xa4" => 0, "\xa5" => 0, "\xa6" => 0, "\xa7" => 0,
"\xa8" => 0, "\xa9" => 0, "\xaa" => 0, "\xab" => 0, "\xac" => 0, "\xad" => 0, "\xae" => 0, "\xaf" => 0,
"\xb0" => 0, "\xb1" => 0, "\xb2" => 0, "\xb3" => 0, "\xb4" => 0, "\xb5" => 0, "\xb6" => 0, "\xb7" => 0,
"\xb8" => 0, "\xb9" => 0, "\xba" => 0, "\xbb" => 0, "\xbc" => 0, "\xbd" => 0, "\xbe" => 0, "\xbf" => 0,
"\xc0" => 2, "\xc1" => 2, "\xc2" => 2, "\xc3" => 2, "\xc4" => 2, "\xc5" => 2, "\xc6" => 2, "\xc7" => 2,
"\xc8" => 2, "\xc9" => 2, "\xca" => 2, "\xcb" => 2, "\xcc" => 2, "\xcd" => 2, "\xce" => 2, "\xcf" => 2,
"\xd0" => 2, "\xd1" => 2, "\xd2" => 2, "\xd3" => 2, "\xd4" => 2, "\xd5" => 2, "\xd6" => 2, "\xd7" => 2,
"\xd8" => 2, "\xd9" => 2, "\xda" => 2, "\xdb" => 2, "\xdc" => 2, "\xdd" => 2, "\xde" => 2, "\xdf" => 2,
"\xe0" => 3, "\xe1" => 3, "\xe2" => 3, "\xe3" => 3, "\xe4" => 3, "\xe5" => 3, "\xe6" => 3, "\xe7" => 3,
"\xe8" => 3, "\xe9" => 3, "\xea" => 3, "\xeb" => 3, "\xec" => 3, "\xed" => 3, "\xee" => 3, "\xef" => 3,
"\xf0" => 4, "\xf1" => 4, "\xf2" => 4, "\xf3" => 4, "\xf4" => 4, "\xf5" => 4, "\xf6" => 4, "\xf7" => 4,
"\xf8" => 5, "\xf9" => 5, "\xfa" => 5, "\xfb" => 5, "\xfc" => 6, "\xfd" => 6, "\xfe" => 0, "\xff" => 0,
);
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param mixed $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars)
{
if (!isset($currentMap['i']) || !isset($currentMap['p'])) {
$currentMap['p'] = $currentMap['i'] = array();
}
$strlen = strlen($string);
$charPos = count($currentMap['p']);
$foundChars = 0;
$invalid = false;
for ($i = 0; $i < $strlen; ++$i) {
$char = $string[$i];
$size = self::$s_length_map[$char];
if ($size == 0) {
/* char is invalid, we must wait for a resync */
$invalid = true;
continue;
} else {
if ($invalid == true) {
/* We mark the chars as invalid and start a new char */
$currentMap['p'][$charPos + $foundChars] = $startOffset + $i;
$currentMap['i'][$charPos + $foundChars] = true;
++$foundChars;
$invalid = false;
}
if (($i + $size) > $strlen) {
$ignoredChars = substr($string, $i);
break;
}
for ($j = 1; $j < $size; ++$j) {
$char = $string[$i + $j];
if ($char > "\x7F" && $char < "\xC0") {
// Valid - continue parsing
} else {
/* char is invalid, we must wait for a resync */
$invalid = true;
continue 2;
}
}
/* Ok we got a complete char here */
$currentMap['p'][$charPos + $foundChars] = $startOffset + $i + $size;
$i += $j - 1;
++$foundChars;
}
}
return $foundChars;
}
/**
* Returns mapType.
*
* @return int mapType
*/
public function getMapType()
{
return self::MAP_TYPE_POSITIONS;
}
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param string $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size)
{
if ($size < 1) {
return -1;
}
$needed = self::$length_map[$bytes[0]] - $size;
return $needed > -1 ? $needed : -1;
}
/**
* Returns the number of bytes which should be read to start each character.
*
* @return int
*/
public function getInitialByteSize()
{
return 1;
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A factory for creating CharacterReaders.
*
* @author Chris Corbyn
*/
interface Swift_CharacterReaderFactory
{
/**
* Returns a CharacterReader suitable for the charset applied.
*
* @param string $charset
*
* @return Swift_CharacterReader
*/
public function getReaderFor($charset);
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Standard factory for creating CharacterReaders.
*
* @author Chris Corbyn
*/
class Swift_CharacterReaderFactory_SimpleCharacterReaderFactory implements Swift_CharacterReaderFactory
{
/**
* A map of charset patterns to their implementation classes.
*
* @var array
*/
private static $_map = array();
/**
* Factories which have already been loaded.
*
* @var Swift_CharacterReaderFactory[]
*/
private static $_loaded = array();
/**
* Creates a new CharacterReaderFactory.
*/
public function __construct()
{
$this->init();
}
public function __wakeup()
{
$this->init();
}
public function init()
{
if (count(self::$_map) > 0) {
return;
}
$prefix = 'Swift_CharacterReader_';
$singleByte = array(
'class' => $prefix.'GenericFixedWidthReader',
'constructor' => array(1),
);
$doubleByte = array(
'class' => $prefix.'GenericFixedWidthReader',
'constructor' => array(2),
);
$fourBytes = array(
'class' => $prefix.'GenericFixedWidthReader',
'constructor' => array(4),
);
// Utf-8
self::$_map['utf-?8'] = array(
'class' => $prefix.'Utf8Reader',
'constructor' => array(),
);
//7-8 bit charsets
self::$_map['(us-)?ascii'] = $singleByte;
self::$_map['(iso|iec)-?8859-?[0-9]+'] = $singleByte;
self::$_map['windows-?125[0-9]'] = $singleByte;
self::$_map['cp-?[0-9]+'] = $singleByte;
self::$_map['ansi'] = $singleByte;
self::$_map['macintosh'] = $singleByte;
self::$_map['koi-?7'] = $singleByte;
self::$_map['koi-?8-?.+'] = $singleByte;
self::$_map['mik'] = $singleByte;
self::$_map['(cork|t1)'] = $singleByte;
self::$_map['v?iscii'] = $singleByte;
//16 bits
self::$_map['(ucs-?2|utf-?16)'] = $doubleByte;
//32 bits
self::$_map['(ucs-?4|utf-?32)'] = $fourBytes;
// Fallback
self::$_map['.*'] = $singleByte;
}
/**
* Returns a CharacterReader suitable for the charset applied.
*
* @param string $charset
*
* @return Swift_CharacterReader
*/
public function getReaderFor($charset)
{
$charset = trim(strtolower($charset));
foreach (self::$_map as $pattern => $spec) {
$re = '/^'.$pattern.'$/D';
if (preg_match($re, $charset)) {
if (!array_key_exists($pattern, self::$_loaded)) {
$reflector = new ReflectionClass($spec['class']);
if ($reflector->getConstructor()) {
$reader = $reflector->newInstanceArgs($spec['constructor']);
} else {
$reader = $reflector->newInstance();
}
self::$_loaded[$pattern] = $reader;
}
return self::$_loaded[$pattern];
}
}
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An abstract means of reading and writing data in terms of characters as opposed
* to bytes.
*
* Classes implementing this interface may use a subsystem which requires less
* memory than working with large strings of data.
*
* @author Chris Corbyn
*/
interface Swift_CharacterStream
{
/**
* Set the character set used in this CharacterStream.
*
* @param string $charset
*/
public function setCharacterSet($charset);
/**
* Set the CharacterReaderFactory for multi charset support.
*
* @param Swift_CharacterReaderFactory $factory
*/
public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory);
/**
* Overwrite this character stream using the byte sequence in the byte stream.
*
* @param Swift_OutputByteStream $os output stream to read from
*/
public function importByteStream(Swift_OutputByteStream $os);
/**
* Import a string a bytes into this CharacterStream, overwriting any existing
* data in the stream.
*
* @param string $string
*/
public function importString($string);
/**
* Read $length characters from the stream and move the internal pointer
* $length further into the stream.
*
* @param int $length
*
* @return string
*/
public function read($length);
/**
* Read $length characters from the stream and return a 1-dimensional array
* containing there octet values.
*
* @param int $length
*
* @return int[]
*/
public function readBytes($length);
/**
* Write $chars to the end of the stream.
*
* @param string $chars
*/
public function write($chars);
/**
* Move the internal pointer to $charOffset in the stream.
*
* @param int $charOffset
*/
public function setPointer($charOffset);
/**
* Empty the stream and reset the internal pointer.
*/
public function flushContents();
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A CharacterStream implementation which stores characters in an internal array.
*
* @author Chris Corbyn
*/
class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStream
{
/** A map of byte values and their respective characters */
private static $_charMap;
/** A map of characters and their derivative byte values */
private static $_byteMap;
/** The char reader (lazy-loaded) for the current charset */
private $_charReader;
/** A factory for creating CharacterReader instances */
private $_charReaderFactory;
/** The character set this stream is using */
private $_charset;
/** Array of characters */
private $_array = array();
/** Size of the array of character */
private $_array_size = array();
/** The current character offset in the stream */
private $_offset = 0;
/**
* Create a new CharacterStream with the given $chars, if set.
*
* @param Swift_CharacterReaderFactory $factory for loading validators
* @param string $charset used in the stream
*/
public function __construct(Swift_CharacterReaderFactory $factory, $charset)
{
self::_initializeMaps();
$this->setCharacterReaderFactory($factory);
$this->setCharacterSet($charset);
}
/**
* Set the character set used in this CharacterStream.
*
* @param string $charset
*/
public function setCharacterSet($charset)
{
$this->_charset = $charset;
$this->_charReader = null;
}
/**
* Set the CharacterReaderFactory for multi charset support.
*
* @param Swift_CharacterReaderFactory $factory
*/
public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory)
{
$this->_charReaderFactory = $factory;
}
/**
* Overwrite this character stream using the byte sequence in the byte stream.
*
* @param Swift_OutputByteStream $os output stream to read from
*/
public function importByteStream(Swift_OutputByteStream $os)
{
if (!isset($this->_charReader)) {
$this->_charReader = $this->_charReaderFactory
->getReaderFor($this->_charset);
}
$startLength = $this->_charReader->getInitialByteSize();
while (false !== $bytes = $os->read($startLength)) {
$c = array();
for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) {
$c[] = self::$_byteMap[$bytes[$i]];
}
$size = count($c);
$need = $this->_charReader
->validateByteSequence($c, $size);
if ($need > 0 &&
false !== $bytes = $os->read($need)) {
for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) {
$c[] = self::$_byteMap[$bytes[$i]];
}
}
$this->_array[] = $c;
++$this->_array_size;
}
}
/**
* Import a string a bytes into this CharacterStream, overwriting any existing
* data in the stream.
*
* @param string $string
*/
public function importString($string)
{
$this->flushContents();
$this->write($string);
}
/**
* Read $length characters from the stream and move the internal pointer
* $length further into the stream.
*
* @param int $length
*
* @return string
*/
public function read($length)
{
if ($this->_offset == $this->_array_size) {
return false;
}
// Don't use array slice
$arrays = array();
$end = $length + $this->_offset;
for ($i = $this->_offset; $i < $end; ++$i) {
if (!isset($this->_array[$i])) {
break;
}
$arrays[] = $this->_array[$i];
}
$this->_offset += $i - $this->_offset; // Limit function calls
$chars = false;
foreach ($arrays as $array) {
$chars .= implode('', array_map('chr', $array));
}
return $chars;
}
/**
* Read $length characters from the stream and return a 1-dimensional array
* containing there octet values.
*
* @param int $length
*
* @return int[]
*/
public function readBytes($length)
{
if ($this->_offset == $this->_array_size) {
return false;
}
$arrays = array();
$end = $length + $this->_offset;
for ($i = $this->_offset; $i < $end; ++$i) {
if (!isset($this->_array[$i])) {
break;
}
$arrays[] = $this->_array[$i];
}
$this->_offset += ($i - $this->_offset); // Limit function calls
return call_user_func_array('array_merge', $arrays);
}
/**
* Write $chars to the end of the stream.
*
* @param string $chars
*/
public function write($chars)
{
if (!isset($this->_charReader)) {
$this->_charReader = $this->_charReaderFactory->getReaderFor(
$this->_charset);
}
$startLength = $this->_charReader->getInitialByteSize();
$fp = fopen('php://memory', 'w+b');
fwrite($fp, $chars);
unset($chars);
fseek($fp, 0, SEEK_SET);
$buffer = array(0);
$buf_pos = 1;
$buf_len = 1;
$has_datas = true;
do {
$bytes = array();
// Buffer Filing
if ($buf_len - $buf_pos < $startLength) {
$buf = array_splice($buffer, $buf_pos);
$new = $this->_reloadBuffer($fp, 100);
if ($new) {
$buffer = array_merge($buf, $new);
$buf_len = count($buffer);
$buf_pos = 0;
} else {
$has_datas = false;
}
}
if ($buf_len - $buf_pos > 0) {
$size = 0;
for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i) {
++$size;
$bytes[] = $buffer[$buf_pos++];
}
$need = $this->_charReader->validateByteSequence(
$bytes, $size);
if ($need > 0) {
if ($buf_len - $buf_pos < $need) {
$new = $this->_reloadBuffer($fp, $need);
if ($new) {
$buffer = array_merge($buffer, $new);
$buf_len = count($buffer);
}
}
for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i) {
$bytes[] = $buffer[$buf_pos++];
}
}
$this->_array[] = $bytes;
++$this->_array_size;
}
} while ($has_datas);
fclose($fp);
}
/**
* Move the internal pointer to $charOffset in the stream.
*
* @param int $charOffset
*/
public function setPointer($charOffset)
{
if ($charOffset > $this->_array_size) {
$charOffset = $this->_array_size;
} elseif ($charOffset < 0) {
$charOffset = 0;
}
$this->_offset = $charOffset;
}
/**
* Empty the stream and reset the internal pointer.
*/
public function flushContents()
{
$this->_offset = 0;
$this->_array = array();
$this->_array_size = 0;
}
private function _reloadBuffer($fp, $len)
{
if (!feof($fp) && ($bytes = fread($fp, $len)) !== false) {
$buf = array();
for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) {
$buf[] = self::$_byteMap[$bytes[$i]];
}
return $buf;
}
return false;
}
private static function _initializeMaps()
{
if (!isset(self::$_charMap)) {
self::$_charMap = array();
for ($byte = 0; $byte < 256; ++$byte) {
self::$_charMap[$byte] = chr($byte);
}
self::$_byteMap = array_flip(self::$_charMap);
}
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A CharacterStream implementation which stores characters in an internal array.
*
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream
{
/**
* The char reader (lazy-loaded) for the current charset.
*
* @var Swift_CharacterReader
*/
private $_charReader;
/**
* A factory for creating CharacterReader instances.
*
* @var Swift_CharacterReaderFactory
*/
private $_charReaderFactory;
/**
* The character set this stream is using.
*
* @var string
*/
private $_charset;
/**
* The data's stored as-is.
*
* @var string
*/
private $_datas = '';
/**
* Number of bytes in the stream.
*
* @var int
*/
private $_datasSize = 0;
/**
* Map.
*
* @var mixed
*/
private $_map;
/**
* Map Type.
*
* @var int
*/
private $_mapType = 0;
/**
* Number of characters in the stream.
*
* @var int
*/
private $_charCount = 0;
/**
* Position in the stream.
*
* @var int
*/
private $_currentPos = 0;
/**
* Constructor.
*
* @param Swift_CharacterReaderFactory $factory
* @param string $charset
*/
public function __construct(Swift_CharacterReaderFactory $factory, $charset)
{
$this->setCharacterReaderFactory($factory);
$this->setCharacterSet($charset);
}
/* -- Changing parameters of the stream -- */
/**
* Set the character set used in this CharacterStream.
*
* @param string $charset
*/
public function setCharacterSet($charset)
{
$this->_charset = $charset;
$this->_charReader = null;
$this->_mapType = 0;
}
/**
* Set the CharacterReaderFactory for multi charset support.
*
* @param Swift_CharacterReaderFactory $factory
*/
public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory)
{
$this->_charReaderFactory = $factory;
}
/**
* @see Swift_CharacterStream::flushContents()
*/
public function flushContents()
{
$this->_datas = null;
$this->_map = null;
$this->_charCount = 0;
$this->_currentPos = 0;
$this->_datasSize = 0;
}
/**
* @see Swift_CharacterStream::importByteStream()
*
* @param Swift_OutputByteStream $os
*/
public function importByteStream(Swift_OutputByteStream $os)
{
$this->flushContents();
$blocks = 512;
$os->setReadPointer(0);
while (false !== ($read = $os->read($blocks))) {
$this->write($read);
}
}
/**
* @see Swift_CharacterStream::importString()
*
* @param string $string
*/
public function importString($string)
{
$this->flushContents();
$this->write($string);
}
/**
* @see Swift_CharacterStream::read()
*
* @param int $length
*
* @return string
*/
public function read($length)
{
if ($this->_currentPos >= $this->_charCount) {
return false;
}
$ret = false;
$length = $this->_currentPos + $length > $this->_charCount ? $this->_charCount - $this->_currentPos : $length;
switch ($this->_mapType) {
case Swift_CharacterReader::MAP_TYPE_FIXED_LEN:
$len = $length * $this->_map;
$ret = substr($this->_datas,
$this->_currentPos * $this->_map,
$len);
$this->_currentPos += $length;
break;
case Swift_CharacterReader::MAP_TYPE_INVALID:
$ret = '';
for (; $this->_currentPos < $length; ++$this->_currentPos) {
if (isset($this->_map[$this->_currentPos])) {
$ret .= '?';
} else {
$ret .= $this->_datas[$this->_currentPos];
}
}
break;
case Swift_CharacterReader::MAP_TYPE_POSITIONS:
$end = $this->_currentPos + $length;
$end = $end > $this->_charCount ? $this->_charCount : $end;
$ret = '';
$start = 0;
if ($this->_currentPos > 0) {
$start = $this->_map['p'][$this->_currentPos - 1];
}
$to = $start;
for (; $this->_currentPos < $end; ++$this->_currentPos) {
if (isset($this->_map['i'][$this->_currentPos])) {
$ret .= substr($this->_datas, $start, $to - $start).'?';
$start = $this->_map['p'][$this->_currentPos];
} else {
$to = $this->_map['p'][$this->_currentPos];
}
}
$ret .= substr($this->_datas, $start, $to - $start);
break;
}
return $ret;
}
/**
* @see Swift_CharacterStream::readBytes()
*
* @param int $length
*
* @return int[]
*/
public function readBytes($length)
{
$read = $this->read($length);
if ($read !== false) {
$ret = array_map('ord', str_split($read, 1));
return $ret;
}
return false;
}
/**
* @see Swift_CharacterStream::setPointer()
*
* @param int $charOffset
*/
public function setPointer($charOffset)
{
if ($this->_charCount < $charOffset) {
$charOffset = $this->_charCount;
}
$this->_currentPos = $charOffset;
}
/**
* @see Swift_CharacterStream::write()
*
* @param string $chars
*/
public function write($chars)
{
if (!isset($this->_charReader)) {
$this->_charReader = $this->_charReaderFactory->getReaderFor(
$this->_charset);
$this->_map = array();
$this->_mapType = $this->_charReader->getMapType();
}
$ignored = '';
$this->_datas .= $chars;
$this->_charCount += $this->_charReader->getCharPositions(substr($this->_datas, $this->_datasSize), $this->_datasSize, $this->_map, $ignored);
if ($ignored !== false) {
$this->_datasSize = strlen($this->_datas) - strlen($ignored);
} else {
$this->_datasSize = strlen($this->_datas);
}
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2009 Fabien Potencier <fabien.potencier@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Base class for Spools (implements time and message limits).
*
* @author Fabien Potencier
*/
abstract class Swift_ConfigurableSpool implements Swift_Spool
{
/** The maximum number of messages to send per flush */
private $_message_limit;
/** The time limit per flush */
private $_time_limit;
/**
* Sets the maximum number of messages to send per flush.
*
* @param int $limit
*/
public function setMessageLimit($limit)
{
$this->_message_limit = (int) $limit;
}
/**
* Gets the maximum number of messages to send per flush.
*
* @return int The limit
*/
public function getMessageLimit()
{
return $this->_message_limit;
}
/**
* Sets the time limit (in seconds) per flush.
*
* @param int $limit The limit
*/
public function setTimeLimit($limit)
{
$this->_time_limit = (int) $limit;
}
/**
* Gets the time limit (in seconds) per flush.
*
* @return int The limit
*/
public function getTimeLimit()
{
return $this->_time_limit;
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* DependencyException gets thrown when a requested dependency is missing.
*
* @author Chris Corbyn
*/
class Swift_DependencyException extends Swift_SwiftException
{
/**
* Create a new DependencyException with $message.
*
* @param string $message
*/
public function __construct($message)
{
parent::__construct($message);
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An embedded file, in a multipart message.
*
* @author Chris Corbyn
*/
class Swift_EmbeddedFile extends Swift_Mime_EmbeddedFile
{
/**
* Create a new EmbeddedFile.
*
* Details may be optionally provided to the constructor.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*/
public function __construct($data = null, $filename = null, $contentType = null)
{
call_user_func_array(
array($this, 'Swift_Mime_EmbeddedFile::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('mime.embeddedfile')
);
$this->setBody($data);
$this->setFilename($filename);
if ($contentType) {
$this->setContentType($contentType);
}
}
/**
* Create a new EmbeddedFile.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*
* @return Swift_Mime_EmbeddedFile
*/
public static function newInstance($data = null, $filename = null, $contentType = null)
{
return new self($data, $filename, $contentType);
}
/**
* Create a new EmbeddedFile from a filesystem path.
*
* @param string $path
*
* @return Swift_Mime_EmbeddedFile
*/
public static function fromPath($path)
{
return self::newInstance()->setFile(
new Swift_ByteStream_FileByteStream($path)
);
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Interface for all Encoder schemes.
*
* @author Chris Corbyn
*/
interface Swift_Encoder extends Swift_Mime_CharsetObserver
{
/**
* Encode a given string to produce an encoded string.
*
* @param string $string
* @param int $firstLineOffset if first line needs to be shorter
* @param int $maxLineLength - 0 indicates the default length for this encoding
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0);
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Base 64 Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Encoder_Base64Encoder implements Swift_Encoder
{
/**
* Takes an unencoded string and produces a Base64 encoded string from it.
*
* Base64 encoded strings have a maximum line length of 76 characters.
* If the first line needs to be shorter, indicate the difference with
* $firstLineOffset.
*
* @param string $string to encode
* @param int $firstLineOffset
* @param int $maxLineLength optional, 0 indicates the default of 76 bytes
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
if (0 >= $maxLineLength || 76 < $maxLineLength) {
$maxLineLength = 76;
}
$encodedString = base64_encode($string);
$firstLine = '';
if (0 != $firstLineOffset) {
$firstLine = substr(
$encodedString, 0, $maxLineLength - $firstLineOffset
)."\r\n";
$encodedString = substr(
$encodedString, $maxLineLength - $firstLineOffset
);
}
return $firstLine.trim(chunk_split($encodedString, $maxLineLength, "\r\n"));
}
/**
* Does nothing.
*/
public function charsetChanged($charset)
{
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles RFC 2231 specified Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Encoder_Rfc2231Encoder implements Swift_Encoder
{
/**
* A character stream to use when reading a string as characters instead of bytes.
*
* @var Swift_CharacterStream
*/
private $_charStream;
/**
* Creates a new Rfc2231Encoder using the given character stream instance.
*
* @param Swift_CharacterStream
*/
public function __construct(Swift_CharacterStream $charStream)
{
$this->_charStream = $charStream;
}
/**
* Takes an unencoded string and produces a string encoded according to
* RFC 2231 from it.
*
* @param string $string
* @param int $firstLineOffset
* @param int $maxLineLength optional, 0 indicates the default of 75 bytes
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
$lines = array();
$lineCount = 0;
$lines[] = '';
$currentLine = &$lines[$lineCount++];
if (0 >= $maxLineLength) {
$maxLineLength = 75;
}
$this->_charStream->flushContents();
$this->_charStream->importString($string);
$thisLineLength = $maxLineLength - $firstLineOffset;
while (false !== $char = $this->_charStream->read(4)) {
$encodedChar = rawurlencode($char);
if (0 != strlen($currentLine)
&& strlen($currentLine.$encodedChar) > $thisLineLength) {
$lines[] = '';
$currentLine = &$lines[$lineCount++];
$thisLineLength = $maxLineLength;
}
$currentLine .= $encodedChar;
}
return implode("\r\n", $lines);
}
/**
* Updates the charset used.
*
* @param string $charset
*/
public function charsetChanged($charset)
{
$this->_charStream->setCharacterSet($charset);
}
/**
* Make a deep copy of object.
*/
public function __clone()
{
$this->_charStream = clone $this->_charStream;
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides quick access to each encoding type.
*
* @author Chris Corbyn
*/
class Swift_Encoding
{
/**
* Get the Encoder that provides 7-bit encoding.
*
* @return Swift_Mime_ContentEncoder
*/
public static function get7BitEncoding()
{
return self::_lookup('mime.7bitcontentencoder');
}
/**
* Get the Encoder that provides 8-bit encoding.
*
* @return Swift_Mime_ContentEncoder
*/
public static function get8BitEncoding()
{
return self::_lookup('mime.8bitcontentencoder');
}
/**
* Get the Encoder that provides Quoted-Printable (QP) encoding.
*
* @return Swift_Mime_ContentEncoder
*/
public static function getQpEncoding()
{
return self::_lookup('mime.qpcontentencoder');
}
/**
* Get the Encoder that provides Base64 encoding.
*
* @return Swift_Mime_ContentEncoder
*/
public static function getBase64Encoding()
{
return self::_lookup('mime.base64contentencoder');
}
private static function _lookup($key)
{
return Swift_DependencyContainer::getInstance()->lookup($key);
}
}
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Generated when a command is sent over an SMTP connection.
*
* @author Chris Corbyn
*/
class Swift_Events_CommandEvent extends Swift_Events_EventObject
{
/**
* The command sent to the server.
*
* @var string
*/
private $_command;
/**
* An array of codes which a successful response will contain.
*
* @var int[]
*/
private $_successCodes = array();
/**
* Create a new CommandEvent for $source with $command.
*
* @param Swift_Transport $source
* @param string $command
* @param array $successCodes
*/
public function __construct(Swift_Transport $source, $command, $successCodes = array())
{
parent::__construct($source);
$this->_command = $command;
$this->_successCodes = $successCodes;
}
/**
* Get the command which was sent to the server.
*
* @return string
*/
public function getCommand()
{
return $this->_command;
}
/**
* Get the numeric response codes which indicate success for this command.
*
* @return int[]
*/
public function getSuccessCodes()
{
return $this->_successCodes;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment