Skip to content

Commit

Permalink
Add email serializer
Browse files Browse the repository at this point in the history
  • Loading branch information
jadb committed Jan 8, 2015
1 parent ade0190 commit e670f75
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 1 deletion.
119 changes: 118 additions & 1 deletion src/Network/Email/Email.php
Expand Up @@ -23,8 +23,15 @@
use Cake\Network\Http\FormData\Part;
use Cake\Utility\Hash;
use Cake\Utility\Text;
use Closure;
use Exception;
use InvalidArgumentException;
use JsonSerializable;
use LogicException;
use PDO;
use RuntimeException;
use Serializable;
use SimpleXmlElement;

/**
* CakePHP email class.
Expand All @@ -40,7 +47,7 @@
* application sends.
*
*/
class Email
class Email implements JsonSerializable, Serializable
{

use StaticConfigTrait;
Expand Down Expand Up @@ -1885,4 +1892,114 @@ protected function _getContentTypeCharset()
}
return strtoupper($this->charset);
}

/**
* Serializes the email object to a value that can be natively serialized and re-used
* to clone this email instance.
*
* It has certain limitations for viewVars that are good to know:
*
* - ORM\Query executed and stored as resultset
* - SimpleXmlElements stored as associative array
* - Exceptions stored as strings
* - Resources, \Closure and \PDO are not supported.
*
* @return array Serializable array of configuration properties.
* @throws \Exception When a view var object can not be properly serialized.
*/
public function jsonSerialize()
{
$properties = [
'_to', '_from', '_sender', '_replyTo', '_cc', '_bcc', '_subject', '_returnPath', '_readReceipt',
'_template', '_layout', '_viewRender', '_viewVars', '_theme', '_helpers', '_emailFormat',
'_emailPattern', '_attachments', '_domain', '_messageId', '_headers', 'charset', 'headerCharset',
];

$array = [];

foreach ($properties as $property) {
$array[$property] = $this->{$property};
}

array_walk($array['_attachments'], function (&$item, $key) {
if (!empty($item['file'])) {
$item['data'] = $this->_readFile($item['file']);
unset($item['file']);
}
});

array_walk_recursive($array['_viewVars'], [$this, '_checkViewVars']);

return array_filter($array, function ($i) {
return !is_array($i) && strlen($i) || !empty($i);
});
}

/**
* Iterates through hash to clean up and normalize.
*
* @param mixed $item Reference to the view var value.
* @param string $key View var key.
* @return void
*/
protected function _checkViewVars(&$item, $key)
{
if ($item instanceof Exception) {
$item = (string)$item;
}

if (
is_resource($item) ||
$item instanceof Closure ||
$item instanceof PDO
) {
throw new RuntimeException(sprintf(
'Failed serializing the `%s` %s in the `%s` view var',
is_resource($item) ? get_resource_type($item) : get_class($item),
is_resource($item) ? 'resource' : 'object',
$key
));
}
}

/**
* Configures an email instance object from serialized config.
*
* @param array $config Email configuration array.
* @return \Cake\Network\Email\Email Configured email instance.
*/
public function createFromArray($config)
{
foreach ($config as $property => $value) {
$this->{$property} = $value;
}

return $this;
}

/**
* Serializes the Email object.
*
* @return void.
*/
public function serialize()
{
$array = $this->jsonSerialize();
array_walk_recursive($array, function (&$item, $key) {
if ($item instanceof SimpleXmlElement) {
$item = json_decode(json_encode((array)$item), true);
}
});
return serialize($array);
}

/**
* Unserializes the Email object.
*
* @return void.
*/
public function unserialize($data)
{
return $this->createFromArray(unserialize($data));
}
}
96 changes: 96 additions & 0 deletions tests/TestCase/Network/Email/EmailTest.php
Expand Up @@ -21,8 +21,10 @@
use Cake\Log\Log;
use Cake\Network\Email\DebugTransport;
use Cake\Network\Email\Email;
use Cake\ORM\TableRegistry;
use Cake\TestSuite\TestCase;
use Cake\View\Exception\MissingTemplateException;
use SimpleXmlElement;

/**
* Help to test Email
Expand Down Expand Up @@ -88,6 +90,8 @@ public function render($content)
class EmailTest extends TestCase
{

public $fixtures = ['core.users'];

/**
* setUp
*
Expand Down Expand Up @@ -2591,6 +2595,98 @@ public function testZeroOnlyLinesNotBeingEmptied()
$this->assertEquals($expected, $result['message']);
}

/**
* testJsonSerialize()
*
* @return void
*/
public function testJsonSerialize()
{
$xmlstr = <<<XML
<?xml version='1.0' standalone='yes'?>
<framework>
<name>CakePHP</name>
<url>http://cakephp.org</url>
</framework>
XML;

$this->CakeEmail->reset()
->to(['cakephp@cakephp.org' => 'CakePHP'])
->from('noreply@cakephp.org')
->replyTo('cakephp@cakephp.org')
->cc(['mark@cakephp.org', 'juan@cakephp.org' => 'Juan Basso'])
->bcc('phpnut@cakephp.org')
->subject('Test Serialize')
->template('default', 'test')
->messageId('<uuid@server.com>')
->domain('foo.bar')
->viewVars([
'users' => TableRegistry::get('Users')->get(1, ['fields' => ['id', 'username']]),
'xml' => new SimpleXmlElement($xmlstr),
'exception' => new \Exception('test')
])
->attachments([
'test.txt' => TEST_APP . 'config' . DS . 'empty.ini',
'image' => [
'data' => file_get_contents(TEST_APP . 'webroot' . DS . 'img' . DS . 'cake.icon.png'),
'mimetype' => 'image/png'
]
]);

$result = json_decode(json_encode($this->CakeEmail), true);
$this->assertContains('test', $result['_viewVars']['exception']);
unset($result['_viewVars']['exception']);

$encode = function ($path) {
return chunk_split(base64_encode(file_get_contents($path)), 76, "\r\n");
};

$expected = [
'_to' => ['cakephp@cakephp.org' => 'CakePHP'],
'_from' => ['noreply@cakephp.org' => 'noreply@cakephp.org'],
'_replyTo' => ['cakephp@cakephp.org' => 'cakephp@cakephp.org'],
'_cc' => ['mark@cakephp.org' => 'mark@cakephp.org', 'juan@cakephp.org' => 'Juan Basso'],
'_bcc' => ['phpnut@cakephp.org' => 'phpnut@cakephp.org'],
'_subject' => 'Test Serialize',
'_template' => 'default',
'_layout' => 'test',
'_viewRender' => 'Cake\View\View',
'_helpers' => ['Html'],
'_emailFormat' => 'text',
'_messageId' => '<uuid@server.com>',
'_domain' => 'foo.bar',
'charset' => 'utf-8',
'headerCharset' => 'utf-8',
'_viewVars' => [
'users' => [
'id' => 1,
'username' => 'mariano'
],
'xml' => [
'name' => 'CakePHP',
'url' => 'http://cakephp.org'
],
],
'_attachments' => [
'test.txt' => [
'data' => $encode(TEST_APP . 'config' . DS . 'empty.ini'),
'mimetype' => 'application/octet-stream'
],
'image' => [
'data' => $encode(TEST_APP . 'webroot' . DS . 'img' . DS . 'cake.icon.png'),
'mimetype' => 'image/png'
]
],
'_emailPattern' => '/^((?:[\p{L}0-9.!#$%&\'*+\/=?^_`{|}~-]+)*@[\p{L}0-9-.]+)$/ui'
];
$this->assertEquals($expected, $result);

$result = json_decode(json_encode(unserialize(serialize($this->CakeEmail))), true);
$this->assertContains('test', $result['_viewVars']['exception']);
unset($result['_viewVars']['exception']);
$this->assertEquals($expected, $result);
}

/**
* CakeEmailTest::assertLineLengths()
*
Expand Down

0 comments on commit e670f75

Please sign in to comment.