Skip to content

Commit

Permalink
Release 3.0.0
Browse files Browse the repository at this point in the history
- Switched from Singleton, because it made testing more complicated and did not provide any improvements compared to a "normal" class
- Added new method setByteCacheMaxLength()
- Updated test
- Updated dependencies (composer.lock)
- Updated readme
  • Loading branch information
SoftCreatR committed Oct 29, 2018
1 parent 9aca177 commit 6826517
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 82 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ Short answer: I have just little experience in unit testing. This project was a
- PHP 7.1 or newer
- [Composer](https://getcomposer.org)

If you are looking for a solution that works on older PHP versions (5.3.2+), head over to the [oldphp](https://github.com/SoftCreatR/php-mime-detector/tree/oldphp) branch.

## Installation

Require this package using [Composer](https://getcomposer.org/), in the root directory of your project:
Expand All @@ -46,7 +48,7 @@ use SoftCreatR\MimeDetector\MimeDetector;
use SoftCreatR\MimeDetector\MimeDetectorException;

// create an instance of the MimeDetector
$mimeDetector = MimeDetector::getInstance();
$mimeDetector = new MimeDetector();

// set our file to read
try {
Expand All @@ -62,6 +64,19 @@ $fileData = $mimeDetector->getFileType();
echo '<pre>' . print_r($fileData, true) . '</pre>';
```

Or short:

```php
use SoftCreatR\MimeDetector\MimeDetector;
use SoftCreatR\MimeDetector\MimeDetectorException;

try {
echo '<pre>' . print_r((new MimeDetector())->setFile('foo.bar')->getFileType(), true) . '</pre>';
} catch (MimeDetectorException $e) {
die('An error occured while trying to load the given file.');
}
```

## Testing

Testing utilizes PHPUnit (what else?) by running this command:
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"issues": "https://github.com/SoftCreatR/php-mime-detector/issues",
"forum": "https://support.softcreatr.com"
},
"version": "2.0.1",
"version": "3.0.0",
"require": {
"php": ">=7.1.0"
},
Expand Down
22 changes: 11 additions & 11 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

110 changes: 43 additions & 67 deletions src/SoftCreatR/MimeDetector/MimeDetector.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,6 @@
*/
class MimeDetector
{
/**
* Current instance
*
* @var MimeDetector
*/
private static $instance;

/**
* Cached first X bytes of the given file
*
Expand All @@ -33,6 +26,13 @@ class MimeDetector
*/
private $byteCacheLen = 0;

/**
* Maximum number of bytes to cache
*
* @var integer
*/
private $maxByteCacheLen = 0;

/**
* Path to the given file
*
Expand Down Expand Up @@ -74,69 +74,20 @@ class MimeDetector
];

/**
* Singletons do not support a public constructor. Override init() if
* you need to initialize components on creation.
*
* @codeCoverageIgnore
*/
final protected function __construct()
{
$this->init();
}

/**
* Called within __construct(), override if necessary.
*
* @codeCoverageIgnore
* Create a new MimeDetector object.
*/
protected function init()
public function __construct()
{
}

/**
* Object cloning is disallowed.
*
* @codeCoverageIgnore
*/
final protected function __clone()
{
}

/**
* Object serializing is disallowed.
*
* @throws MimeDetectorException
* @codeCoverageIgnore
*/
final public function __sleep()
{
throw new MimeDetectorException('Serializing of Singletons is not allowed');
}

/**
* Returns an unique instance of the MimeDetector class.
*
* @return MimeDetector
*/
public static function getInstance(): MimeDetector
{
// @codeCoverageIgnoreStart
if (empty(self::$instance)) {
self::$instance = new MimeDetector();
}
// @codeCoverageIgnoreEnd

return self::$instance;
}

/**
* Setter for the file to be checked.
*
* @param string $filePath
* @return MimeDetector
* @throws MimeDetectorException
*/
public function setFile(string $filePath): MimeDetector
public function setFile(string $filePath): self
{
if (!file_exists($filePath)) {
throw new MimeDetectorException("File '" . $filePath . "' does not exist.");
Expand All @@ -147,6 +98,7 @@ public function setFile(string $filePath): MimeDetector
if ($this->fileHash !== $fileHash) {
$this->byteCache = [];
$this->byteCacheLen = 0;
$this->maxByteCacheLen = $this->maxByteCacheLen ?: 4096;
$this->file = $filePath;
$this->fileHash = $fileHash;

Expand Down Expand Up @@ -249,12 +201,11 @@ public function getFileType(): array
// Need to be before the `zip` check
if ($this->checkForBytes([0x50, 0x4B, 0x3, 0x4])) {
if ($this->checkForBytes([
0x6D, 0x69, 0x6D, 0x65, 0x74, 0x79, 0x70,
0x65, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x65,
0x70, 0x75, 0x62, 0x2B, 0x7A, 0x69, 0x70
], 30)
) {
0x6D, 0x69, 0x6D, 0x65, 0x74, 0x79, 0x70,
0x65, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x65,
0x70, 0x75, 0x62, 0x2B, 0x7A, 0x69, 0x70
], 30)) {
return [
'ext' => 'epub',
'mime' => 'application/epub+zip'
Expand Down Expand Up @@ -1112,6 +1063,30 @@ public function toBytes(string $str): array
{
return array_values(unpack('C*', $str));
}

/**
* Allows to define the max byte cache length for a file. This can be useful in cases,
* when you expect files, where the magic number should be found within the first X bytes.
* However, the byte cache should have at least a length of 4 for a proper detection.
*
* @param int $maxLength
* @return MimeDetector
* @throws MimeDetectorException
*/
public function setByteCacheMaxLength(int $maxLength): self
{
if ($this->byteCacheLen > 0) {
throw new MimeDetectorException('setByteCacheMaxLength() must be called before setFile().');
}

if ($maxLength < 4) {
throw new MimeDetectorException('Maximum byte cache length must not be smaller than 4.');
}

$this->maxByteCacheLen = $maxLength;

return $this;
}

/**
* Checks the byte sequence of a given string.
Expand Down Expand Up @@ -1181,7 +1156,8 @@ protected function checkForBytes(array $bytes, int $offset = 0, array $mask = []
}

/**
* Caches the first 4096 bytes of the given file, so we don't have to read the whole file on every iteration.
* Caches the first X bytes (4096 by default) of the given file,
* so we don't have to read the whole file on every iteration.
*
* @return void
* @throws MimeDetectorException
Expand All @@ -1197,7 +1173,7 @@ protected function createByteCache(): void
}

$handle = fopen($this->file, 'rb');
$data = fread($handle, 4096);
$data = fread($handle, $this->maxByteCacheLen);
fclose($handle);

foreach (str_split($data) as $i => $char) {
Expand Down
45 changes: 43 additions & 2 deletions tests/SoftCreatR/MimeDetector/MimeDetectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class MimeDetectorTest extends TestCaseImplementation
*/
public function getInstance(): MimeDetector
{
return MimeDetector::getInstance();
return new MimeDetector();
}

/**
Expand Down Expand Up @@ -57,6 +57,7 @@ public function testSetFile($testFiles): void

self::assertAttributeNotEmpty('byteCache', $mimeDetector);
self::assertAttributeGreaterThanOrEqual(1, 'byteCacheLen', $mimeDetector);
self::assertAttributeEquals(4096, 'maxByteCacheLen', $mimeDetector);
self::assertAttributeSame($testFile['file'], 'file', $mimeDetector);
self::assertAttributeSame($testFile['hash'], 'fileHash', $mimeDetector);
}
Expand Down Expand Up @@ -185,7 +186,7 @@ public function testGetHashFile(): void
{
self::assertNotFalse($this->getInstance()->getHash(__FILE__));
}

/**
* @return void
*/
Expand All @@ -202,6 +203,46 @@ public function testToBytes(): void
self::assertEquals([112, 104, 112], $this->getInstance()->toBytes('php'));
}

/**
* Test, if `setByteCacheMaxLength` throws an exception, when being called too late.
*
* @return void
* @throws MimeDetectorException
*/
public function testSetByteCacheMaxLengthThrowsExceptionWrongOrder(): void
{
$this->expectException(MimeDetectorException::class);
$this->getInstance()->setFile(__FILE__)->setByteCacheMaxLength(123);
}

/**
* Test, if `setByteCacheMaxLength` throws an exception, if the given max length is too small.
*
* @return void
* @throws MimeDetectorException
*/
public function testSetByteCacheMaxLengthThrowsExceptionTooSmall(): void
{
$this->expectException(MimeDetectorException::class);
$this->getInstance()->setByteCacheMaxLength(3);
}

/**
* @return void
* @throws MimeDetectorException
*/
public function testSetByteCacheMaxLength(): void
{
$mimeDetector = $this->getInstance();

$mimeDetector->setByteCacheMaxLength(5);
$mimeDetector->setFile(__FILE__);

self::assertAttributeEquals(5, 'maxByteCacheLen', $mimeDetector);
self::assertAttributeEquals(5, 'byteCacheLen', $mimeDetector);
self::assertAttributeSame($mimeDetector->toBytes('<?php'), 'byteCache', $mimeDetector);
}

/**
* @return void
* @throws ReflectionException
Expand Down

0 comments on commit 6826517

Please sign in to comment.