diff --git a/README.md b/README.md index 03853c1..ee96cf8 100644 --- a/README.md +++ b/README.md @@ -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: @@ -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 { @@ -62,6 +64,19 @@ $fileData = $mimeDetector->getFileType(); echo '
' . print_r($fileData, true) . ''; ``` +Or short: + +```php +use SoftCreatR\MimeDetector\MimeDetector; +use SoftCreatR\MimeDetector\MimeDetectorException; + +try { + echo '
' . print_r((new MimeDetector())->setFile('foo.bar')->getFileType(), true) . ''; +} catch (MimeDetectorException $e) { + die('An error occured while trying to load the given file.'); +} +``` + ## Testing Testing utilizes PHPUnit (what else?) by running this command: diff --git a/composer.json b/composer.json index b406ced..00779a6 100644 --- a/composer.json +++ b/composer.json @@ -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" }, diff --git a/composer.lock b/composer.lock index 40e7826..212e93f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "86c202427e508e71d19affd6572bb086", + "content-hash": "68f3aceae3815997393b434b1d86f58c", "packages": [], "packages-dev": [ { @@ -428,16 +428,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "6.1.1", + "version": "6.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "b097681a19a48e52706f57e47a09594bac4f7cab" + "reference": "4d3ae9b21a7d7e440bd0cf65565533117976859f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b097681a19a48e52706f57e47a09594bac4f7cab", - "reference": "b097681a19a48e52706f57e47a09594bac4f7cab", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4d3ae9b21a7d7e440bd0cf65565533117976859f", + "reference": "4d3ae9b21a7d7e440bd0cf65565533117976859f", "shasum": "" }, "require": { @@ -487,7 +487,7 @@ "testing", "xunit" ], - "time": "2018-10-18T09:01:38+00:00" + "time": "2018-10-23T05:59:32+00:00" }, { "name": "phpunit/php-file-iterator", @@ -680,16 +680,16 @@ }, { "name": "phpunit/phpunit", - "version": "7.4.1", + "version": "7.4.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c5a120ade60992bd671a912188ee9ee9f8083bbd" + "reference": "c151651fb6ed264038d486ea262e243af72e5e64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c5a120ade60992bd671a912188ee9ee9f8083bbd", - "reference": "c5a120ade60992bd671a912188ee9ee9f8083bbd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c151651fb6ed264038d486ea262e243af72e5e64", + "reference": "c151651fb6ed264038d486ea262e243af72e5e64", "shasum": "" }, "require": { @@ -760,7 +760,7 @@ "testing", "xunit" ], - "time": "2018-10-18T09:02:52+00:00" + "time": "2018-10-23T05:57:41+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", diff --git a/src/SoftCreatR/MimeDetector/MimeDetector.php b/src/SoftCreatR/MimeDetector/MimeDetector.php index 08e9a94..bfad6b6 100644 --- a/src/SoftCreatR/MimeDetector/MimeDetector.php +++ b/src/SoftCreatR/MimeDetector/MimeDetector.php @@ -12,13 +12,6 @@ */ class MimeDetector { - /** - * Current instance - * - * @var MimeDetector - */ - private static $instance; - /** * Cached first X bytes of the given file * @@ -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 * @@ -74,61 +74,12 @@ 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. * @@ -136,7 +87,7 @@ public static function getInstance(): MimeDetector * @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."); @@ -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; @@ -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' @@ -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. @@ -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 @@ -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) { diff --git a/tests/SoftCreatR/MimeDetector/MimeDetectorTest.php b/tests/SoftCreatR/MimeDetector/MimeDetectorTest.php index 914565a..a4854f7 100644 --- a/tests/SoftCreatR/MimeDetector/MimeDetectorTest.php +++ b/tests/SoftCreatR/MimeDetector/MimeDetectorTest.php @@ -19,7 +19,7 @@ class MimeDetectorTest extends TestCaseImplementation */ public function getInstance(): MimeDetector { - return MimeDetector::getInstance(); + return new MimeDetector(); } /** @@ -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); } @@ -185,7 +186,7 @@ public function testGetHashFile(): void { self::assertNotFalse($this->getInstance()->getHash(__FILE__)); } - + /** * @return void */ @@ -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('