Skip to content

Commit

Permalink
add feature: detect mime type from file using finfo
Browse files Browse the repository at this point in the history
  • Loading branch information
frederikbosch committed Jun 11, 2017
1 parent 7c761d7 commit bd1c1cf
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/Header/ContentType.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public function getValue(): HeaderValue
/**
* @return ContentType
*/
public static function unknown()
public static function unknown(): ContentType
{
return new self('application/octet-stream', '');
}
Expand Down
76 changes: 76 additions & 0 deletions src/Header/HeaderValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
*/
final class HeaderValue
{
/**
*
*/
private CONST PARSE_START = 1;
/**
*
*/
private CONST PARSE_QUOTE = 2;
/**
* @var string
*/
Expand Down Expand Up @@ -129,4 +137,72 @@ public static function fromEncodedString(string $value): HeaderValue
$headerValue->needsEncoding = false;
return $headerValue;
}

/**
* @param string $headerValueAsString
* @return HeaderValue
*/
public static function fromString(string $headerValueAsString): HeaderValue
{
$values = [];

$headerValueAsString = trim($headerValueAsString);

$length = strlen($headerValueAsString) - 1;
$n = -1;
$state = self::PARSE_START;
$escapeNext = false;
$sequence = '';

while ($n < $length) {
$n++;

$char = $headerValueAsString[$n];

$sequence .= $char;

if ($char === '\\') {
$escapeNext = true;
continue;
}

if ($escapeNext) {
$escapeNext = false;
continue;
}

switch ($state) {
case self::PARSE_QUOTE:
if ($char === '"') {
$state = self::PARSE_START;
}

break;
default:
if ($char === '"') {
$state = self::PARSE_QUOTE;
}

if ($char === ';') {
$values[] = trim(substr($sequence, 0, -1));
$sequence = '';
$state = self::PARSE_START;
}
break;
}
}

$values[] = trim($sequence);

$headerValue = new self($values[0]);

$parameters = [];
foreach (array_slice($values, 1) as $parameterString) {
$parameter = HeaderValueParameter::fromString($parameterString);
$parameters[$parameter->getName()] = $parameter;
}

$headerValue->parameters = $parameters;
return $headerValue;
}
}
22 changes: 22 additions & 0 deletions src/Header/HeaderValueParameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,26 @@ public function __toString(): string
{
return sprintf('%s="%s"', $this->name, $this->value);
}

/**
* @param string $parameterString
* @return HeaderValueParameter
*/
public static function fromString(string $parameterString): HeaderValueParameter
{
$nameValue = explode('=', $parameterString);
if (count($nameValue) !== 2) {
throw new \InvalidArgumentException(
sprintf('Invalid parameter string value %s', $parameterString)
);
}

[$name, $value] = $nameValue;

if ($value[0] === '"' && $value[-1] === '"') {
$value = substr($value, 1, -1);
}

return new self($name, $value);
}
}
31 changes: 31 additions & 0 deletions src/Mime/FileAttachment.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Genkgo\Mail\Header\ContentDisposition;
use Genkgo\Mail\Header\ContentTransferEncoding;
use Genkgo\Mail\Header\ContentType;
use Genkgo\Mail\Header\HeaderValue;
use Genkgo\Mail\HeaderInterface;
use Genkgo\Mail\Stream\Base64EncodedStream;
use Genkgo\Mail\StreamInterface;
Expand Down Expand Up @@ -41,6 +42,36 @@ public function __construct(string $filename, ContentType $contentType, string $
->withHeader(new ContentTransferEncoding('base64'));
}

/**
* @param string $filename
* @param string $attachmentName
* @return FileAttachment
*/
public static function fromUnknownFileType(string $filename, string $attachmentName = ''): FileAttachment
{
$fileInfo = new \finfo(FILEINFO_MIME);
$mime = $fileInfo->file($filename);

$headerValue = HeaderValue::fromString($mime);

try {
$charset = $headerValue->getParameter('charset')->getValue();
if ($charset === 'binary') {
$contentType = new ContentType($headerValue->getRaw());
} else {
$contentType = new ContentType($headerValue->getRaw(), $charset);
}
} catch (\UnexpectedValueException $e) {
$contentType = new ContentType($headerValue->getRaw());
}

return new self(
$filename,
$contentType,
$attachmentName
);
}

/**
* @return iterable
*/
Expand Down
Binary file added test/Stub/minimal.pdf
Binary file not shown.
45 changes: 45 additions & 0 deletions test/Unit/Header/HeaderValueParameterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);

namespace Genkgo\TestMail\Unit\Header;

use Genkgo\Mail\Header\HeaderValueParameter;
use Genkgo\TestMail\AbstractTestCase;

final class HeaderValueParameterTest extends AbstractTestCase
{

/**
* @test
*/
public function it_can_parse_a_string()
{
$parameter = HeaderValueParameter::fromString('charset="utf-8"');

$this->assertEquals((string)$parameter, 'charset="utf-8"');
$this->assertEquals($parameter->getName(), 'charset');
$this->assertEquals($parameter->getValue(), 'utf-8');
}

/**
* @test
*/
public function it_can_parse_an_unquoted_string()
{
$parameter = HeaderValueParameter::fromString('charset=utf-8');

$this->assertEquals((string)$parameter, 'charset="utf-8"');
$this->assertEquals($parameter->getName(), 'charset');
$this->assertEquals($parameter->getValue(), 'utf-8');
}

/**
* @test
*/
public function it_does_not_parse_invalid_values()
{
$this->expectException(\InvalidArgumentException::class);
HeaderValueParameter::fromString('charset,utf-8');
}

}
23 changes: 23 additions & 0 deletions test/Unit/Header/HeaderValueTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,27 @@ public function it_overwrites_parameters()

$this->assertEquals('value; name1="value2"', (string)$header);
}

/**
* @test
*/
public function it_can_parse_a_string()
{
$header = HeaderValue::fromString('application/pdf; charset="utf-8"');

$this->assertEquals('application/pdf; charset="utf-8"', (string)$header);
$this->assertEquals('charset="utf-8"', (string)$header->getParameter('charset'));
}

/**
* @test
*/
public function it_can_parse_a_string_with_more_than_one_parameter()
{
$header = HeaderValue::fromString('application/pdf; charset="utf-8"; foo="bar"');

$this->assertEquals('application/pdf; charset="utf-8"; foo="bar"', (string)$header);
$this->assertEquals('charset="utf-8"', (string)$header->getParameter('charset'));
$this->assertEquals('foo="bar"', (string)$header->getParameter('foo'));
}
}
15 changes: 15 additions & 0 deletions test/Unit/Mime/FileAttachmentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,19 @@ public function it_encodes_body_with_base64()

$this->assertInstanceOf(Base64EncodedStream::class, $attachment->getBody());
}

/**
* @test
*/
public function it_is_able_to_detect_mime_type()
{
$attachment = FileAttachment::fromUnknownFileType(
__DIR__ .'/../../Stub/minimal.pdf'
);

$this->assertEquals(
'application/pdf; charset="utf-8"',
(string) $attachment->getHeader('Content-Type')->getValue()
);
}
}

0 comments on commit bd1c1cf

Please sign in to comment.