diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9a142b6..d3278ee 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -7,11 +7,11 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- php: ['8.1', '8.2']
+ php: ['8.1', '8.2', '8.3']
name: Testing on PHP ${{ matrix.php }}
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -27,4 +27,7 @@ jobs:
run: vendor/bin/phpunit
- name: Run phpstan
- run: vendor/bin/phpstan analyze --no-progress --level=5 src/
+ run: vendor/bin/phpstan
+
+ - name: Validate coding standards
+ run: vendor/bin/phpcs
diff --git a/Dockerfile b/Dockerfile
index b319775..e169d89 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,17 @@
-FROM php:8-cli
+FROM php:8.1-cli
+
+# install dependencies
+RUN apt update \
+ && apt install -y \
+ libicu-dev \
+ git \
+ zip \
+ && pecl install xdebug \
+ && docker-php-ext-enable \
+ xdebug \
+ && docker-php-ext-install \
+ intl \
+ && apt-get clean
# install composer
-#
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
diff --git a/README.md b/README.md
index 191cf27..36d37cb 100755
--- a/README.md
+++ b/README.md
@@ -26,12 +26,18 @@ Here are some code samples, to show how the library is handled.
use Intervention\MimeSniffer\MimeSniffer;
use Intervention\MimeSniffer\Types\ImageJpeg;
-// detect given string
+// universal factory method
+$sniffer = MimeSniffer::create($content);
+
+// or detect given string
$sniffer = MimeSniffer::createFromString($content);
// or detect given file
$sniffer = MimeSniffer::createFromFilename('image.jpg');
+// or detect from file pointer
+$sniffer = MimeSniffer::createFromFilename(fopen('test.jpg', 'r'));
+
// returns object of detected type
$type = $sniffer->getType();
@@ -65,6 +71,9 @@ $type = $sniffer->setFromString($other_content)->getType();
// or with setter for filename
$type = $sniffer->setFromFilename('images/image.jpg')->getType();
+
+// or with setter for file pointer
+$type = $sniffer->setFromPointer(fopen('images/image.jpg', 'r'))->getType();
```
**Currently only the following file types can be detected. More will be added in a next release.**
diff --git a/composer.json b/composer.json
index 5841224..4ac9ab3 100644
--- a/composer.json
+++ b/composer.json
@@ -17,11 +17,13 @@
}
],
"require": {
- "php": "^7.3|^8.0"
+ "php": "^8.1"
},
"require-dev": {
"phpunit/phpunit": "^10.0",
- "phpstan/phpstan": "^1"
+ "phpstan/phpstan": "^1",
+ "squizlabs/php_codesniffer": "^3.8",
+ "slevomat/coding-standard": "~8.0"
},
"autoload": {
"psr-4": {
@@ -30,8 +32,13 @@
},
"autoload-dev": {
"psr-4": {
- "Intervention\\MimeSniffer\\Test\\": "tests"
+ "Intervention\\MimeSniffer\\Tests\\": "tests"
}
},
- "minimum-stability": "stable"
+ "minimum-stability": "stable",
+ "config": {
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ }
+ }
}
diff --git a/docker-compose.yml b/docker-compose.yml
index 4cca254..648a1aa 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -10,6 +10,12 @@ services:
analysis:
build: ./
working_dir: /project
- command: bash -c "composer install && ./vendor/bin/phpstan analyze --level=4 ./src"
+ command: bash -c "composer install && ./vendor/bin/phpstan analyze ./src"
+ volumes:
+ - ./:/project
+ standards:
+ build: ./
+ working_dir: /project
+ command: bash -c "composer install && ./vendor/bin/phpcs"
volumes:
- ./:/project
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
new file mode 100644
index 0000000..226f59a
--- /dev/null
+++ b/phpcs.xml.dist
@@ -0,0 +1,68 @@
+
+
+ src/
+ tests/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/phpstan.dist.neon b/phpstan.dist.neon
new file mode 100644
index 0000000..906ac9e
--- /dev/null
+++ b/phpstan.dist.neon
@@ -0,0 +1,9 @@
+parameters:
+ level: 6
+ paths:
+ - src
+ exceptions:
+ check:
+ missingCheckedExceptionInThrows: true
+ uncheckedExceptionClasses:
+ - Error
diff --git a/src/AbstractBinaryType.php b/src/AbstractBinaryType.php
index 3cad5d5..8b72e0f 100644
--- a/src/AbstractBinaryType.php
+++ b/src/AbstractBinaryType.php
@@ -1,5 +1,7 @@
setFromString($content);
+ if (is_string($content) && file_exists($content)) {
+ $this->setFromFilename($content);
+ }
+
+ if (is_string($content)) {
+ $this->setFromString($content);
+ }
+
+ if (is_resource($content)) {
+ $this->setFromPointer($content);
+ }
+ }
+
+ /**
+ * Universal factory method
+ *
+ * @param mixed $content
+ * @throws InvalidArgumentException
+ * @return MimeSniffer
+ */
+ public static function create(mixed $content): self
+ {
+ return new self($content);
}
/**
* Create new instance from given string
*
* @param string $content
- *
+ * @throws InvalidArgumentException
* @return MimeSniffer
*/
- public static function createFromString(string $content): MimeSniffer
+ public static function createFromString(string $content): self
{
- return new self($content);
+ return (new self())->setFromString($content);
}
/**
* Load contents of given string into instance
*
* @param string $content
- *
+ * @throws InvalidArgumentException
* @return MimeSniffer
*/
- public function setFromString(string $content): MimeSniffer
+ public function setFromString(string $content): self
{
- $this->content = strval($content);
+ $this->content = $content;
return $this;
}
@@ -51,10 +80,10 @@ public function setFromString(string $content): MimeSniffer
* Create a new instance and load contents of given filename
*
* @param string $filename
- *
+ * @throws InvalidArgumentException
* @return MimeSniffer
*/
- public static function createFromFilename(string $filename): MimeSniffer
+ public static function createFromFilename(string $filename): self
{
return (new self())->setFromFilename($filename);
}
@@ -63,10 +92,10 @@ public static function createFromFilename(string $filename): MimeSniffer
* Load contents of given filename in current instance
*
* @param string $filename
- *
+ * @throws InvalidArgumentException
* @return MimeSniffer
*/
- public function setFromFilename(string $filename): MimeSniffer
+ public function setFromFilename(string $filename): self
{
$fp = fopen($filename, 'r');
$this->setFromString(fread($fp, 1024));
@@ -75,12 +104,42 @@ public function setFromFilename(string $filename): MimeSniffer
return $this;
}
+ /**
+ * Create a new instance and load contents of given filename
+ *
+ * @param resource $pointer
+ * @throws InvalidArgumentException
+ * @return MimeSniffer
+ */
+ public static function createFromPointer($pointer): self
+ {
+ return (new self())->setFromPointer($pointer);
+ }
+
+ /**
+ * Load contents of given filename in current instance
+ *
+ * @throws InvalidArgumentException
+ * @param resource $pointer
+ * @return MimeSniffer
+ */
+ public function setFromPointer($pointer): self
+ {
+ if (!is_resource($pointer)) {
+ throw new InvalidArgumentException('Argument #1 $pointer must be of type resource.');
+ }
+
+ $this->setFromString(fread($pointer, 1024));
+
+ return $this;
+ }
+
/**
* Return detected type
*
- * @return AbstractType
+ * @return TypeInterface
*/
- public function getType(): AbstractType
+ public function getType(): TypeInterface
{
foreach ($this->getTypeClassnames() as $classname) {
$type = new $classname();
@@ -98,30 +157,34 @@ public function getType(): AbstractType
}
/**
- * Determine if content matches the given type
- * or any if the given types in array
+ * Determine if content matches the given type or any if the given types in array
*
- * @param AbstractType|array $types AbstractType or array of AbstractTypes
- * @return boolean
+ * @param TypeInterface|string|array $types
+ * @return bool
*/
- public function matches($types): bool
+ public function matches(TypeInterface|string|array $types): bool
{
- if (! is_array($types)) {
+ if (!is_array($types)) {
$types = [$types];
}
- $types = array_map(function ($value) {
- if (is_a($value, AbstractType::class)) {
- return $value;
- }
+ $types = array_filter($types, function ($type) {
+ return match (true) {
+ ($type instanceof TypeInterface) => true,
+ is_string($type) && class_exists($type) => true,
+ default => false,
+ };
+ });
- if (!is_null($value) && class_exists($value)) {
- return new $value();
- }
+ $types = array_map(function ($type) {
+ return match (true) {
+ is_string($type) => new $type(),
+ default => $type,
+ };
}, $types);
$types = array_filter($types, function ($type) {
- return is_a($type, AbstractType::class);
+ return $type instanceof TypeInterface;
});
foreach ($types as $type) {
@@ -136,7 +199,7 @@ public function matches($types): bool
/**
* Return array of type classnames
*
- * @return array
+ * @return array
*/
private function getTypeClassnames(): array
{
@@ -147,7 +210,7 @@ private function getTypeClassnames(): array
}, $files);
return array_filter($classnames, function ($classname) {
- return ! in_array($classname, [
+ return !in_array($classname, [
Types\ApplicationOctetStream::class,
Types\TextPlain::class,
]);
diff --git a/src/Types/ApplicationGzip.php b/src/Types/ApplicationGzip.php
index 4a0a103..6ae2423 100644
--- a/src/Types/ApplicationGzip.php
+++ b/src/Types/ApplicationGzip.php
@@ -1,5 +1,7 @@
getMockForAbstractClass(AbstractType::class);
- $this->assertEquals('', $type);
+ $this->assertEquals('', (string) $this->getTestAbstractType());
}
public function testMatches(): void
{
- $type = $this->getMockForAbstractClass(AbstractType::class);
- $this->assertFalse($type->matches('test'));
+ $this->assertFalse($this->getTestAbstractType()->matches('test'));
}
public function testIsImage(): void
{
- $type = $this->getMockForAbstractClass(AbstractType::class);
- $this->assertFalse($type->isImage());
+ $this->assertFalse($this->getTestAbstractType()->isImage());
}
public function testPrepareContent(): void
@@ -32,13 +31,18 @@ public function testPrepareContent(): void
$content .= 'x';
}
- $type = $this->getMockForAbstractClass(AbstractType::class);
- $this->assertEquals(1024, strlen($type->prepareContent($content)));
+ $this->assertEquals(1024, strlen($this->getTestAbstractType()->prepareContent($content)));
}
public function testIsBinary(): void
{
- $type = $this->getMockForAbstractClass(AbstractType::class);
- $this->assertFalse($type->isBinary());
+ $this->assertFalse($this->getTestAbstractType()->isBinary());
+ }
+
+ private function getTestAbstractType(): AbstractType
+ {
+ return new class () extends AbstractType
+ {
+ };
}
}
diff --git a/tests/ApplicationGzipTest.php b/tests/ApplicationGzipTest.php
index e99e434..b57020e 100644
--- a/tests/ApplicationGzipTest.php
+++ b/tests/ApplicationGzipTest.php
@@ -1,6 +1,8 @@
assertInstanceOf(MimeSniffer::class, $sniffer);
- $sniffer = new MimeSniffer();
+ $sniffer = new MimeSniffer(
+ __DIR__ . '/../tests/files/test.jpg',
+ );
$this->assertInstanceOf(MimeSniffer::class, $sniffer);
+
+ $sniffer = new MimeSniffer(
+ fopen(__DIR__ . '/../tests/files/test.jpg', 'r')
+ );
+ $this->assertInstanceOf(MimeSniffer::class, $sniffer);
+
+ $sniffer = new MimeSniffer(
+ 'foo'
+ );
+ $this->assertInstanceOf(MimeSniffer::class, $sniffer);
+ }
+
+ public function testCreate(): void
+ {
+ $this->assertInstanceOf(MimeSniffer::class, MimeSniffer::create(
+ __DIR__ . '/../tests/files/test.jpg',
+ ));
+
+ $this->assertInstanceOf(MimeSniffer::class, MimeSniffer::create(
+ 'foo'
+ ));
+
+ $this->assertInstanceOf(MimeSniffer::class, MimeSniffer::create(
+ fopen(__DIR__ . '/../tests/files/test.jpg', 'r'),
+ ));
+
+ $this->assertInstanceOf(MimeSniffer::class, MimeSniffer::create(
+ null
+ ));
}
public function testCreateFromString(): void
@@ -34,6 +66,12 @@ public function testCreateFromFilename(): void
$this->assertInstanceOf(MimeSniffer::class, $sniffer);
}
+ public function testCreateFromPointer(): void
+ {
+ $sniffer = MimeSniffer::createFromPointer(fopen(__DIR__ . '/../tests/files/test.jpg', 'r'));
+ $this->assertInstanceOf(MimeSniffer::class, $sniffer);
+ }
+
public function testSetFromString(): void
{
$sniffer = new MimeSniffer();
@@ -48,6 +86,13 @@ public function testSetFromFilename(): void
$this->assertInstanceOf(MimeSniffer::class, $sniffer);
}
+ public function testSetFromPointer(): void
+ {
+ $sniffer = new MimeSniffer();
+ $sniffer = $sniffer->setFromPointer(fopen(__DIR__ . '/../tests/stubs/zip', 'r'));
+ $this->assertInstanceOf(MimeSniffer::class, $sniffer);
+ }
+
public function testMatchesType(): void
{
$sniffer = MimeSniffer::createFromFilename(__DIR__ . '/../tests/files/test.gif');
@@ -62,6 +107,10 @@ public function testMatchesArray(): void
$this->assertFalse($sniffer->matches([new ImageJpeg(), new ImagePng()]));
$this->assertTrue($sniffer->matches([ImageJpeg::class, ImagePng::class, ImageGif::class]));
$this->assertFalse($sniffer->matches([ImageJpeg::class, ImagePng::class]));
+ $this->assertFalse($sniffer->matches([ImageJpeg::class, new ImagePng()]));
+ $this->assertFalse($sniffer->matches(['foo', new stdClass()]));
+ $this->assertTrue($sniffer->matches(['foo', new stdClass(), new ImageGif()]));
+ $this->assertTrue($sniffer->matches(['foo', new stdClass(), ImageGif::class]));
}
public function testMatchesBogus(): void
@@ -70,7 +119,6 @@ public function testMatchesBogus(): void
$this->assertFalse($sniffer->matches('foo'));
$this->assertFalse($sniffer->matches(['foo', 'bar']));
$this->assertFalse($sniffer->matches([]));
- $this->assertFalse($sniffer->matches(null));
$this->assertTrue($sniffer->matches(['foo', ApplicationZip::class]));
}
}
diff --git a/tests/VideoMpegTest.php b/tests/VideoMpegTest.php
index 4a14e26..00722fd 100644
--- a/tests/VideoMpegTest.php
+++ b/tests/VideoMpegTest.php
@@ -1,6 +1,8 @@