default services conflit ?

This commit is contained in:
armansansd
2022-04-27 11:30:43 +02:00
parent 28190a5749
commit 8bb1064a3b
8132 changed files with 900138 additions and 426 deletions

View File

@@ -0,0 +1,4 @@
template: |
## Whats Changed
$CHANGES

View File

@@ -0,0 +1,55 @@
name: Tests
on:
push:
branches: [ master ]
schedule:
- cron: "0 6 * * 3"
pull_request:
branches: [ master ]
jobs:
php-version:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-version:
- "5.6"
- "7.0"
- "7.1"
- "7.2"
- "7.3"
- "7.4"
- "8.0"
- "8.1"
steps:
- uses: actions/checkout@v2
- name: Install PHP
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
coverage: "none"
ini-values: "zend.assertions=1"
- name: Install Composer dependencies
run: composer install --no-progress --ansi
- name: Run tests
run: ./vendor/bin/phpunit --color=always
- name: Mapping test
if: ${{ matrix.php-version == 7.4 }}
continue-on-error: true
run: |
php ./bin/fileeye-mimemap --version
php ./bin/fileeye-mimemap update --diff --fail-on-diff --ansi
- name: Code style test
if: ${{ matrix.php-version == 7.4 }}
run: ./vendor/bin/phpcs --runtime-set ignore_warnings_on_exit 1

2
old.vendor/fileeye/mimemap/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
vendor/
composer.lock

View File

@@ -0,0 +1,18 @@
build:
environment:
php: '7.4'
nodes:
analysis:
tests:
override:
- php-scrutinizer-run
-
command: ./vendor/bin/phpunit --coverage-clover build/logs/clover.xml
coverage:
file: build/logs/clover.xml
format: clover
checks:
php:
code_rating: true
duplication: true

View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@@ -0,0 +1,227 @@
# MimeMap
[![Tests](https://github.com/FileEye/MimeMap/actions/workflows/php.yml/badge.svg)](https://github.com/FileEye/MimeMap/actions/workflows/php.yml)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/FileEye/MimeMap/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/FileEye/MimeMap/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/FileEye/MimeMap/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/FileEye/MimeMap/?branch=master)
[![Latest Stable Version](https://poser.pugx.org/fileeye/mimemap/v/stable)](https://packagist.org/packages/fileeye/mimemap)
[![Total Downloads](https://poser.pugx.org/fileeye/mimemap/downloads)](https://packagist.org/packages/fileeye/mimemap)
[![License](https://poser.pugx.org/fileeye/mimemap/license)](https://packagist.org/packages/fileeye/mimemap)
A PHP library to handle MIME Content-Type fields and their related file
extensions.
## Features
- Parses MIME Content-Type fields
- Supports the [RFC 2045](https://www.ietf.org/rfc/rfc2045.txt) specification
- Provides utility functions for working with and determining info about MIME
types
- Map file extensions to MIME types and vice-versa
- Automatically update the mapping between MIME types and file extensions from
the most authoritative sources available, [Apache's documentation](http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?view=log)
and the [freedesktop.org project](http://freedesktop.org).
- PHPUnit tested, 100% test coverage
## Credits
MimeMap is a fork of PEAR's [MIME_Type](https://github.com/pear/MIME_Type) package.
See all the [original contributors](https://github.com/pear/MIME_Type/graphs/contributors).
Note that in comparison with PEAR's MIME_Type, this library has a different
scope, mainly focused on finding the mapping between each MIME type and its
generally accepted file extensions.
Features to detect the MIME type of a file have been removed. The [symfony/http-foundation](https://github.com/symfony/http-foundation)
library and its [MimeTypeGuesser](https://api.symfony.com/master/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.html)
API are the suggested components to cover that use case.
### Alternative packages
MimeMap's main difference from similar packages is that it provides
functionalities to use multiple type-to-extension maps and to change the
mapping either at runtime or statically in PHP classes.
See [wgenial/php-mimetyper](https://github.com/wgenial/php-mimetyper#other-php-libraries-for-mime-types)
for a nice list of alternative PHP libraries for MIME type handling.
## Installation
```
$ composer require fileeye/mimemap
```
## Usage
### Basic
The package comes with a default map that describes MIME types and the file
extensions normally associated to each MIME type.
The map also stores information about MIME type _aliases_, (alternative
_media/subtype_ combinations that describe the same MIME type), and the
descriptions of most MIME types and of the acronyms used.
For example: the MIME type _'application/pdf'_
* is described as _'PDF document'_
* the PDF acronym is described as _'PDF: Portable Document Format'_
* is normally using a file extension _'pdf'_
* has aliases such as _'application/x-pdf'_, _'image/pdf'_
The API the package implements is pretty straightforward:
1. You have a MIME type, and want to get the file extensions normally associated
to it:
```php
use FileEye\MimeMap\Type;
...
$type = new Type('image/jpeg');
print_r($type->getExtensions());
// will print ['jpeg', 'jpg', 'jpe']
print_r($type->getDefaultExtension());
// will print 'jpeg'
// When passing an alias to a MIME type, the API will
// return the extensions to the parent type:
$type = new Type('image/pdf');
print_r($type->getDefaultExtension());
// will print 'pdf' which is the default extension for 'application/pdf'
```
2. Viceversa, you have a file extensions, and want to get the MIME type normally
associated to it:
```php
use FileEye\MimeMap\Extension;
...
$ext = new Extension('xar');
print_r($ext->getTypes());
// will return ['application/vnd.xara', 'application/x-xar']
print_r($ext->getDefaultType());
// will return 'application/vnd.xara'
```
3. You have a raw MIME Content-Type string and want to add a parameter:
```php
use FileEye\MimeMap\Type;
...
$type = new Type('text / (Unstructured text) plain ; charset = (UTF8, not ASCII) utf-8');
$type->addParameter('lang', 'it', 'Italian');
echo $type->toString(Type::SHORT_TEXT);
// will print 'text/plain'
echo $type->toString(Type::FULL_TEXT);
// will print 'text/plain; charset="utf-8"; lang="it"'
echo $type->toString(Type::FULL_TEXT_WITH_COMMENTS);
// will print 'text/plain (Unstructured text); charset="utf-8" (UTF8, not ASCII), lang="it" (Italian)'
```
4. You have a MIME Content-Type string and want to the type's description as a comment:
```php
use FileEye\MimeMap\Type;
...
$type = new Type('text/html');
$type_desc = $type->getDescription();
$type->setSubTypeComment($type_desc);
echo $type->toString(Type::FULL_TEXT_WITH_COMMENTS);
// will print 'text/html (HTML document)'
// Setting the $include_acronym parameter of getDescription to true
// will extend the description to include the meaning of the acronym
$type_desc = $type->getDescription(true);
$type->setSubTypeComment($type_desc);
echo $type->toString(Type::FULL_TEXT_WITH_COMMENTS);
// will print 'text/html (HTML document, HTML: HyperText Markup Language)'
```
### Specify alternative MIME type mapping
You can also alter the default map at runtime, either by adding/removing
mappings, or indicating to MimeMap to use a totally different map. The
alternative map must be stored in a PHP class that extends from
`\FileEye\MimeMap\Map\AbstractMap`.
1. You want to add an additional MIME type to extension mapping to the
default class:
```php
use FileEye\MimeMap\Extension;
use FileEye\MimeMap\MapHandler;
use FileEye\MimeMap\Type;
...
$map = MapHandler::map();
$map->addTypeExtensionMapping('foo/bar', 'baz');
$type = new Type('foo/bar');
$default_extension = $type->getDefaultExtension();
// will return 'baz'
$ext = new Extension('baz');
$default_type = $ext->getDefaultExtension();
// will return 'foo/bar'
```
2. You want to set an alternative map class as default:
```php
use FileEye\MimeMap\Extension;
use FileEye\MimeMap\MapHandler;
use FileEye\MimeMap\Type;
...
MapHandler::setDefaultMapClass('MyProject\MyMap');
...
```
3. You can also use the alternative map just for a single Type or Extension
object:
```php
use FileEye\MimeMap\Extension;
use FileEye\MimeMap\Type;
...
$type = new Type('foo/bar', 'MyProject\MyMap');
$ext = new Extension('baz', 'MyProject\MyMap');
```
## Development
### Updating the extension mapping code
The default extension-to-type mapping class can be updated from the sources'
code repositories, using the `fileeye-mimemap` utility:
```
$ cd [project_directory]/vendor/bin
$ fileeye-mimemap update
```
By default, the utility fetches a mapping source available from the [Apache's documentation](http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?view=co)
website, merges it with another mapping source from the [freedesktop.org project](https://gitlab.freedesktop.org/xdg/shared-mime-info/-/blob/master/data/freedesktop.org.xml.in),
then integrates the result with any overrides specified in the
`resources/default_map_build.yml` file, and finally updates the PHP file where
the `\FileEye\MimeMap\Map\DefaultMap` class is stored.
The `--script` and `--class` options allow specifying a different update logic
and a different class file to update. Type
```
$ fileeye-mimemap update --help
```
to get more information.

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env php
<?php
/**
* A Symfony CLI utility application for MimeMap.
*/
use FileEye\MimeMap\Command\UpdateCommand;
use FileEye\MimeMap\Version;
use Symfony\Component\Console\Application;
$packageAutoloader = __DIR__ . '/../vendor/autoload.php';
$standaloneAutoloader = __DIR__ . '/../../../autoload.php';
if (file_exists($packageAutoloader)) {
require_once $packageAutoloader;
} else {
require_once $standaloneAutoloader;
}
$application = new Application('fileeye-mimemap', Version::get());
$application->add(new UpdateCommand());
$application->run();

View File

@@ -0,0 +1,38 @@
{
"name": "fileeye/mimemap",
"type": "library",
"description": "A PHP library to handle MIME Content-Type fields and their related file extensions.",
"keywords": ["mime", "mime-type", "mime-database", "mime-parser"],
"homepage": "https://github.com/FileEye/MimeMap",
"license": "LGPL-3.0-or-later",
"require": {
"php": ">=5.6",
"composer-runtime-api": "^2.0.0"
},
"require-dev": {
"phpunit/phpunit": "^5 | ^6 | ^7 | ^8 | ^9",
"squizlabs/php_codesniffer": "*",
"sebastian/comparator": "*",
"sebastian/diff": "*",
"symfony/filesystem": "*",
"symfony/console": "*",
"symfony/var-dumper": "*",
"symfony/yaml": "*"
},
"autoload": {
"psr-4": {
"FileEye\\MimeMap\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"FileEye\\MimeMap\\Test\\": "tests/src/"
}
},
"bin": ["bin/fileeye-mimemap"],
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* Examples for the README file.
*/
use FileEye\MimeMap\Type;
use FileEye\MimeMap\Extension;
require_once __DIR__ . '/../vendor/autoload.php';
// -------------------
$type = new Type('image/jpeg');
print_r($type->getExtensions());
// will print ['jpeg', 'jpg', 'jpe']
print_r($type->getDefaultExtension());
// will print 'jpeg'
// When passing an alias to a MIME type, the API will
// return the extensions to the parent type:
$type = new Type('image/pdf');
print_r($type->getDefaultExtension());
// will print 'pdf' which is the default extension for 'application/pdf'
// -------------------
$ext = new Extension('xar');
print_r($ext->getTypes());
// will return ['application/vnd.xara', 'application/x-xar']
print_r($ext->getDefaultType());
// will return 'application/vnd.xara'
// -------------------
$type = new Type('text / (Unstructured text) plain ; charset = (UTF8, not ASCII) utf-8');
$type->addParameter('lang', 'it', 'Italian');
echo $type->toString(Type::SHORT_TEXT);
// will print 'text/plain'
echo $type->toString(Type::FULL_TEXT);
// will print 'text/plain; charset="utf-8"; lang="it"'
echo $type->toString(Type::FULL_TEXT_WITH_COMMENTS);
// will print 'text/plain (Unstructured text); charset="utf-8" (UTF8, not ASCII), lang="it" (Italian)'
// -------------------
$type = new Type('text/html');
$type_desc = $type->getDescription();
$type->setSubTypeComment($type_desc);
echo $type->toString(Type::FULL_TEXT_WITH_COMMENTS);
// will print 'text/html (HTML document)'
// Setting the $include_acronym parameter of getDescription to true
// will extend the description to include the meaning of the acronym
$type_desc = $type->getDescription(true);
$type->setSubTypeComment($type_desc);
echo $type->toString(Type::FULL_TEXT_WITH_COMMENTS);
// will print 'text/html (HTML document, HTML: HyperText Markup Language)'

View File

@@ -0,0 +1,25 @@
<?xml version="1.0"?>
<ruleset>
<arg name="basepath" value="."/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="80"/>
<arg name="cache" value=".phpcs-cache"/>
<arg name="colors" />
<!-- Show progress of the run -->
<arg value="p"/>
<file>src</file>
<file>tests</file>
<rule ref="PSR2"/>
<!-- Allow long lines -->
<rule ref="Generic.Files.LineLength">
<severity>0</severity>
</rule>
<!-- Disallow short array syntax -->
<rule ref="Generic.Arrays.DisallowLongArraySyntax.Found">
<type>error</type>
</rule>
</ruleset>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnFailure="false"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="FileEye Testing Suite">
<directory suffix="Test.php">./tests/</directory>
</testsuite>
</testsuites>
<!-- Filter for coverage reports. -->
<filter>
<whitelist>
<directory suffix=".php">./src/</directory>
<exclude>
<directory suffix=".php">./src/Command/</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@@ -0,0 +1,106 @@
# MimeMap
#
# This file provides the default script to build the MIME-type to file extension
# map. It is used as input to the 'fileeye-mimemap' utility.
#
# The script fetches a mapping source available from the Apache's documentation
# website, merges it with another mapping source from the freedesktop.org
# project, integrates the result with any overrides specified by
# 'applyOverrides', and finally updates the PHP file where the
# '\FileEye\MimeMap\Map\DefaultMap' class is stored.
#
# The entries are executed sequentially; each entry indicates a MapUpdater
# method to be invoked and the arguments to be passed in.
# The Apache httpd project contains the most complete list of file extension to
# mime type mapping on the planet. We use it to update our own list.
-
- 'Loading MIME type information from svn.apache.org'
- loadMapFromApacheFile
- [http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?view=co]
# Extend Apache table with the Freedesktop.org database.
-
- 'Removing redundant svn.apache.org types'
- applyOverrides
-
-
- [removeType, [application/x-msaccess]]
- [removeType, [application/vnd.ms-xpsdocument]]
- [removeType, [application/vnd.stardivision.writer-global]]
- [removeType, [application/x-bzip2]]
- [removeType, [application/x-cbr]]
- [removeType, [application/x-iso9660-image]]
- [removeType, [application/x-chess-pgn]]
- [removeType, [application/x-debian-package]]
- [removeType, [application/java-archive]]
- [removeType, [application/java-vm]]
- [removeType, [application/x-lzh-compressed]]
- [removeType, [application/x-pkcs12]]
- [removeType, [application/x-rar-compressed]]
- [removeType, [application/x-shockwave-flash]]
- [removeType, [application/vnd.smaf]]
- [removeType, [application/x-gtar]]
- [removeType, [audio/x-flac]]
- [removeType, [audio/x-aac]]
- [removeType, [video/x-m4v]]
- [removeType, [video/x-ms-wvx]]
- [removeType, [video/x-ms-wmx]]
- [removeType, [audio/x-pn-realaudio]]
- [removeType, [application/vnd.rn-realmedia-vbr]]
- [removeType, [application/docbook+xml]]
- [removeType, [image/g3fax]]
- [removeType, [image/x-icon]]
- [removeType, [image/x-pcx]]
- [removeType, [application/x-msmetafile]]
- [removeType, [text/x-vcalendar]]
- [removeType, [text/x-vcard]]
- [removeType, [text/x-opml]]
- [removeType, [text/x-c]]
- [removeType, [application/x-tex]]
- [removeType, [video/x-fli]]
- [removeType, [video/x-ms-wm]]
- [removeType, [video/x-ms-asf]]
-
- 'Updating with information from freedesktop.org'
- loadMapFromFreedesktopFile
- [https://gitlab.freedesktop.org/xdg/shared-mime-info/raw/master/data/freedesktop.org.xml.in]
-
- 'Cleanup video/x-anim'
- applyOverrides
-
-
- [removeTypeExtensionMapping, [application/x-troff-man, "[1-9]"]]
- [removeTypeExtensionMapping, [video/x-anim, "anim[1-9j]"]]
- [addTypeExtensionMapping, [video/x-anim, anim1]]
- [addTypeExtensionMapping, [video/x-anim, anim2]]
- [addTypeExtensionMapping, [video/x-anim, anim3]]
- [addTypeExtensionMapping, [video/x-anim, anim4]]
- [addTypeExtensionMapping, [video/x-anim, anim5]]
- [addTypeExtensionMapping, [video/x-anim, anim6]]
- [addTypeExtensionMapping, [video/x-anim, anim7]]
- [addTypeExtensionMapping, [video/x-anim, anim8]]
- [addTypeExtensionMapping, [video/x-anim, anim9]]
- [addTypeExtensionMapping, [video/x-anim, animj]]
-
- 'Adding back selected svn.apache.org mappings'
- applyOverrides
-
-
- [addTypeExtensionMapping, [application/x-bzip, boz]]
- [addTypeExtensionMapping, [application/vnd.comicbook-rar, cba]]
- [addTypeExtensionMapping, [text/x-csrc, dic]]
- [addTypeExtensionMapping, [image/wmf, emz]]
- [addTypeExtensionMapping, [application/vnd.ms-asf, wm]]
# MimeMap overrides.
-
- 'Applying MimeMap overrides'
- applyOverrides
-
-
- [setExtensionDefaultType, [sub, text/vnd.dvb.subtitle]]

View File

@@ -0,0 +1,170 @@
<?php
namespace FileEye\MimeMap\Command;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Comparator\Factory;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Yaml\Yaml;
use FileEye\MimeMap\Map\AbstractMap;
use FileEye\MimeMap\MapHandler;
use FileEye\MimeMap\MapUpdater;
/**
* A Symfony application command to update the MIME type to extension map.
*/
class UpdateCommand extends Command
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('update')
->setDescription('Updates the MIME-type-to-extension map. Executes the commands in the script file specified by --script, then writes the map to the PHP file where the PHP --class is defined.')
->addOption(
'script',
null,
InputOption::VALUE_REQUIRED,
'File name of the script containing the sequence of commands to execute to build the default map.',
MapUpdater::getDefaultMapBuildFile()
)
->addOption(
'class',
null,
InputOption::VALUE_REQUIRED,
'The fully qualified class name of the PHP class storing the map.',
MapHandler::DEFAULT_MAP_CLASS
)
->addOption(
'diff',
null,
InputOption::VALUE_NONE,
'Report updates.'
)
->addOption(
'fail-on-diff',
null,
InputOption::VALUE_NONE,
'Exit with an error when a difference is found. Map will not be updated.'
)
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$updater = new MapUpdater();
$updater->selectBaseMap(MapUpdater::DEFAULT_BASE_MAP_CLASS);
// Executes on the base map the script commands.
$commands = Yaml::parse(file_get_contents($input->getOption('script')));
foreach ($commands as $command) {
$output->writeln("<info>{$command[0]} ...</info>");
try {
$errors = call_user_func_array([$updater, $command[1]], $command[2]);
if (!empty($errors)) {
foreach ($errors as $error) {
$output->writeln("<comment>$error.</comment>");
}
}
} catch (\Exception $e) {
$io->error($e->getMessage());
return(1);
}
}
// Load the map to be changed.
MapHandler::setDefaultMapClass($input->getOption('class'));
$current_map = MapHandler::map();
// Check if anything got changed.
$write = true;
if ($input->getOption('diff')) {
$write = false;
foreach ([
't' => 'MIME types',
'a' => 'MIME type aliases',
'e' => 'extensions',
] as $key => $desc) {
try {
$output->writeln("<info>Checking changes to {$desc} ...</info>");
$this->compareMaps($current_map, $updater->getMap(), $key);
} catch (\RuntimeException $e) {
$output->writeln("<comment>Changes to {$desc} mapping:</comment>");
$output->writeln($e->getMessage());
$write = true;
}
}
}
// Fail on diff if required.
if ($write && $input->getOption('diff') && $input->getOption('fail-on-diff')) {
$io->error('Changes to mapping detected and --fail-on-diff requested, aborting.');
return(2);
}
// If changed, save the new map to the PHP file.
if ($write) {
$updater->writeMapToPhpClassFile($current_map->getFileName());
$output->writeln('<comment>Code updated.</comment>');
} else {
$output->writeln('<info>No changes to mapping.</info>');
}
// Reset the new map's map array.
$updater->getMap()->reset();
return(0);
}
/**
* Compares two type-to-extension maps by section.
*
* @param AbstractMap $old_map
* The first map to compare.
* @param AbstractMap $new_map
* The second map to compare.
* @param string $section
* The first-level array key to compare: 't' or 'e' or 'a'.
*
* @throws \RuntimeException with diff details if the maps differ.
*
* @return bool
* True if the maps are equal.
*/
protected function compareMaps(AbstractMap $old_map, AbstractMap $new_map, $section)
{
$old_map->sort();
$new_map->sort();
$old = $old_map->getMapArray();
$new = $new_map->getMapArray();
$factory = new Factory;
$comparator = $factory->getComparatorFor($old[$section], $new[$section]);
try {
$comparator->assertEquals($old[$section], $new[$section]);
return true;
} catch (ComparisonFailure $failure) {
$old_string = var_export($old[$section], true);
$new_string = var_export($new[$section], true);
if (class_exists('\SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder')) {
$differ = new Differ(new UnifiedDiffOutputBuilder("--- Removed\n+++ Added\n"));
throw new \RuntimeException($differ->diff($old_string, $new_string));
} else {
throw new \RuntimeException(' ');
}
}
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace FileEye\MimeMap;
/**
* Class for mapping file extensions to MIME types.
*/
class Extension
{
/**
* The file extension.
*
* @var string
*/
protected $extension;
/**
* The MIME types map.
*
* @var Map\AbstractMap
*/
protected $map;
/**
* Constructor.
*
* @param string $extension
* A file extension.
* @param string $map_class
* (Optional) The FQCN of the map class to use.
*/
public function __construct($extension, $map_class = null)
{
$this->extension = strtolower($extension);
$this->map = MapHandler::map($map_class);
}
/**
* Returns the file extension's preferred MIME type.
*
* @param bool $strict
* (Optional) if true a MappingException is thrown when no mapping is
* found, if false it returns 'application/octet-stream' as a default.
* Defaults to true.
*
* @throws MappingException if no mapping found and $strict is true.
*
* @return string
*/
public function getDefaultType($strict = true)
{
return $this->getTypes($strict)[0];
}
/**
* Returns all the MIME types related to the file extension.
*
* @param bool $strict
* (Optional) if true a MappingException is thrown when no mapping is
* found, if false it returns ['application/octet-stream'] as a default.
* Defaults to true.
*
* @throws MappingException if no mapping found and $strict is true.
*
* @return string[]
*/
public function getTypes($strict = true)
{
$types = $this->map->getExtensionTypes($this->extension);
if (empty($types)) {
if ($strict) {
throw new MappingException('No MIME type mapped to extension ' . $this->extension);
} else {
return ['application/octet-stream'];
}
}
return $types;
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace FileEye\MimeMap;
/**
* Exception thrown when a MIME type string is malformed.
*/
class MalformedTypeException extends \RuntimeException
{
}

View File

@@ -0,0 +1,450 @@
<?php
namespace FileEye\MimeMap\Map;
use FileEye\MimeMap\MalformedTypeException;
use FileEye\MimeMap\MappingException;
use FileEye\MimeMap\TypeParser;
/**
* Abstract class for mapping file extensions to MIME types.
*
* This class cannot be instantiated; extend from it to implement a map.
*/
abstract class AbstractMap extends BaseMap
{
/**
* Normalizes a mime-type string to Media/Subtype.
*
* @param string $type_string
* MIME type string to parse.
*
* @throws MalformedTypeException when $type_string is malformed.
*
* @return string
* A MIME type string in the 'Media/Subtype' format.
*/
protected function normalizeType($type_string)
{
// Media and SubType are separated by a slash '/'.
$media = TypeParser::parseStringPart($type_string, 0, '/');
if (!$media['string']) {
throw new MalformedTypeException('Media type not found');
}
if (!$media['delimiter_matched']) {
throw new MalformedTypeException('Slash \'/\' to separate media type and subtype not found');
}
// SubType and Parameters are separated by semicolons ';'.
$sub = TypeParser::parseStringPart($type_string, $media['end_offset'] + 1, ';');
if (!$sub['string']) {
throw new MalformedTypeException('Media subtype not found');
}
return strtolower((string) $media['string']) . '/' . strtolower((string) $sub['string']);
}
/**
* Determines if a MIME type exists.
*
* @param string $type The type to be found.
*
* @return bool
*/
public function hasType($type)
{
$type = $this->normalizeType($type);
return (bool) $this->getMapEntry('t', $type);
}
/**
* Determines if a MIME type alias exists.
*
* @param string $alias The alias to be found.
*
* @return bool
*/
public function hasAlias($alias)
{
$alias = $this->normalizeType($alias);
return (bool) $this->getMapEntry('a', $alias);
}
/**
* Determines if an entry exists from the 'extensions' array.
*
* @param string $extension The extension to be found.
*
* @return bool
*/
public function hasExtension($extension)
{
$extension = strtolower($extension);
return (bool) $this->getMapEntry('e', $extension);
}
/**
* Lists all the MIME types defined in the map.
*
* @param string $match (Optional) a match wildcard to limit the list.
*
* @return string[]
*/
public function listTypes($match = null)
{
return $this->listEntries('t', $match);
}
/**
* Lists all the MIME types aliases defined in the map.
*
* @param string $match (Optional) a match wildcard to limit the list.
*
* @return string[]
*/
public function listAliases($match = null)
{
return $this->listEntries('a', $match);
}
/**
* Lists all the extensions defined in the map.
*
* @param string $match (Optional) a match wildcard to limit the list.
*
* @return string[]
*/
public function listExtensions($match = null)
{
return $this->listEntries('e', $match);
}
/**
* Adds a description of a MIME type.
*
* @param string $type
* A MIME type.
* @param string $description
* The description of the MIME type.
*
* @throws MappingException if $type is an alias.
*
* @return $this
*/
public function addTypeDescription($type, $description)
{
$type = $this->normalizeType($type);
// Consistency checks.
if ($this->hasAlias($type)) {
throw new MappingException("Cannot add description for '{$type}', '{$type}' is an alias");
}
$this->addMapSubEntry('t', $type, 'desc', $description);
return $this;
}
/**
* Adds an alias of a MIME type.
*
* @param string $type
* A MIME type.
* @param string $alias
* An alias of $type.
*
* @throws MappingException if no $type is found.
*
* @return $this
*/
public function addTypeAlias($type, $alias)
{
$type = $this->normalizeType($type);
$alias = $this->normalizeType($alias);
// Consistency checks.
if (!$this->hasType($type)) {
throw new MappingException("Cannot set '{$alias}' as alias for '{$type}', '{$type}' not defined");
}
if ($this->hasType($alias)) {
throw new MappingException("Cannot set '{$alias}' as alias for '{$type}', '{$alias}' is already defined as a type");
}
if ($this->hasAlias($alias)) {
$unaliased_types = $this->getAliasTypes($alias);
if (!empty($unaliased_types) && $unaliased_types[0] !== $type) {
throw new MappingException("Cannot set '{$alias}' as alias for '{$type}', it is an alias of '{$unaliased_types[0]}' already");
}
return $this;
}
$this->addMapSubEntry('t', $type, 'a', $alias);
$this->addMapSubEntry('a', $alias, 't', $type);
return $this;
}
/**
* Adds a type-to-extension mapping.
*
* @param string $type
* A MIME type.
* @param string $extension
* A file extension.
*
* @throws MappingException if $type is an alias.
*
* @return $this
*/
public function addTypeExtensionMapping($type, $extension)
{
$type = $this->normalizeType($type);
$extension = strtolower($extension);
// Consistency checks.
if ($this->hasAlias($type)) {
throw new MappingException("Cannot map '{$extension}' to '{$type}', '{$type}' is an alias");
}
// Add entry to 't'.
$this->addMapSubEntry('t', $type, 'e', $extension);
// Add entry to 'e'.
$this->addMapSubEntry('e', $extension, 't', $type);
return $this;
}
/**
* Gets the descriptions of a MIME type.
*
* @param string $type The type to be found.
*
* @return string[] The mapped descriptions.
*/
public function getTypeDescriptions($type)
{
$type = $this->normalizeType($type);
return $this->getMapSubEntry('t', $type, 'desc') ?: [];
}
/**
* Gets the aliases of a MIME type.
*
* @param string $type The type to be found.
*
* @return string[] The mapped aliases.
*/
public function getTypeAliases($type)
{
$type = $this->normalizeType($type);
return $this->getMapSubEntry('t', $type, 'a') ?: [];
}
/**
* Gets the content of an entry from the 't' array.
*
* @param string $type The type to be found.
*
* @return string[] The mapped file extensions.
*/
public function getTypeExtensions($type)
{
$type = $this->normalizeType($type);
return $this->getMapSubEntry('t', $type, 'e') ?: [];
}
/**
* Changes the default extension for a MIME type.
*
* @param string $type
* A MIME type.
* @param string $extension
* A file extension.
*
* @throws MappingException if no mapping found.
*
* @return $this
*/
public function setTypeDefaultExtension($type, $extension)
{
$type = $this->normalizeType($type);
$extension = strtolower($extension);
return $this->setValueAsDefault('t', $type, 'e', $extension);
}
/**
* Removes the entire mapping of a type.
*
* @param string $type
* A MIME type.
*
* @return bool
* true if the mapping was removed, false if the type was not present.
*/
public function removeType($type)
{
$type = $this->normalizeType($type);
// Return false if type is not found.
if (!$this->hasType($type)) {
return false;
}
// Loop through all the associated extensions and remove them.
foreach ($this->getTypeExtensions($type) as $extension) {
$this->removeTypeExtensionMapping($type, $extension);
}
// Loop through all the associated aliases and remove them.
foreach ($this->getTypeAliases($type) as $alias) {
$this->removeTypeAlias($type, $alias);
}
unset(static::$map['t'][$type]);
return true;
}
/**
* Removes a MIME type alias.
*
* @param string $type
* A MIME type.
* @param string $alias
* The alias to be removed.
*
* @return bool
* true if the alias was removed, false if the alias was not present.
*/
public function removeTypeAlias($type, $alias)
{
$type = $this->normalizeType($type);
$alias = $this->normalizeType($alias);
// Remove any extension mapped to the alias.
if ($extensions = $this->getMapSubEntry('a', $alias, 'e')) {
foreach ($extensions as $extension) {
$this->removeMapSubEntry('a', $alias, 'e', $extension);
$this->removeMapSubEntry('e', $extension, 't', $alias);
}
}
$type_ret = $this->removeMapSubEntry('t', $type, 'a', $alias);
$alias_ret = $this->removeMapSubEntry('a', $alias, 't', $type);
return $type_ret && $alias_ret;
}
/**
* Removes a type-to-extension mapping.
*
* @param string $type
* A MIME type.
* @param string $extension
* The file extension to be removed.
*
* @return bool
* true if the mapping was removed, false if the mapping was not present.
*/
public function removeTypeExtensionMapping($type, $extension)
{
$type = $this->normalizeType($type);
$extension = strtolower($extension);
if ($this->hasAlias($type)) {
$alias = $type;
$type_ret = $this->removeMapSubEntry('a', $alias, 'e', $extension);
$extension_ret = $this->removeMapSubEntry('e', $extension, 't', $alias);
} else {
$this->removeAliasedTypesExtensionMapping($type, $extension);
$type_ret = $this->removeMapSubEntry('t', $type, 'e', $extension);
$extension_ret = $this->removeMapSubEntry('e', $extension, 't', $type);
}
return $type_ret && $extension_ret;
}
/**
* Removes aliased types extension mapping.
*
* @param string $type
* A MIME type.
* @param string $extension
* The file extension to be removed.
*/
protected function removeAliasedTypesExtensionMapping($type, $extension)
{
$type = $this->normalizeType($type);
$extension = strtolower($extension);
foreach ($this->getExtensionTypes($extension) as $associated_type) {
if ($this->hasAlias($associated_type) && $type === $this->getAliasTypes($associated_type)[0]) {
$this->removeMapSubEntry('a', $associated_type, 'e', $extension);
$this->removeMapSubEntry('e', $extension, 't', $associated_type);
}
}
}
/**
* Gets the parent types of an alias.
*
* There should not be multiple types for an alias.
*
* @param string $alias The alias to be found.
*
* @return string[]
*/
public function getAliasTypes($alias)
{
$alias = $this->normalizeType($alias);
return $this->getMapSubEntry('a', $alias, 't') ?: [];
}
/**
* Gets the content of an entry from the 'extensions' array.
*
* @param string $extension The extension to be found.
*
* @return string[] The mapped MIME types.
*/
public function getExtensionTypes($extension)
{
$extension = strtolower($extension);
return $this->getMapSubEntry('e', $extension, 't') ?: [];
}
/**
* Changes the default MIME type for a file extension.
*
* Allows a MIME type alias to be set as default for the extension.
*
* @param string $extension
* A file extension.
* @param string $type
* A MIME type.
*
* @throws MappingException if no mapping found.
*
* @return $this
*/
public function setExtensionDefaultType($extension, $type)
{
$type = $this->normalizeType($type);
$extension = strtolower($extension);
if ($this->hasAlias($type)) {
$alias = $type;
$type = $this->getAliasTypes($alias)[0];
// Check that the alias is referring to a type associated with that
// extension.
$associated_types = $this->getMapSubEntry('e', $extension, 't') ?: [];
if (!in_array($type, $associated_types)) {
throw new MappingException("Cannot set '{$alias}' as default type for extension '{$extension}', its unaliased type '{$type}' is not associated to '{$extension}'");
}
$this->addMapSubEntry('a', $alias, 'e', $extension);
$this->addMapSubEntry('e', $extension, 't', $alias);
return $this->setValueAsDefault('e', $extension, 't', $alias);
} else {
return $this->setValueAsDefault('e', $extension, 't', $type);
}
}
}

View File

@@ -0,0 +1,283 @@
<?php
namespace FileEye\MimeMap\Map;
use FileEye\MimeMap\MappingException;
/**
* Abstract base class for managing MimeMap maps.
*
* This class cannot be instantiated; extend from it to implement a map.
*/
abstract class BaseMap
{
/**
* Mapping between file extensions and MIME types.
*
* @var array
*/
protected static $map = [];
/**
* A backup of the mapping between file extensions and MIME types.
*
* Used during the map update process.
*
* @var array
*/
protected static $backupMap;
/**
* Backs up the map array.
*
* @return array
*/
public function backup()
{
static::$backupMap = static::$map;
}
/**
* Resets the map array to the backup.
*
* @return array
*/
public function reset()
{
static::$map = static::$backupMap;
static::$backupMap = null;
}
/**
* Returns the singleton.
*
* @return string
*/
public static function getInstance()
{
if (!static::$instance) {
static::$instance = new static();
}
return static::$instance;
}
/**
* Returns this file's full qualified filename.
*
* @return string
*/
public function getFileName()
{
throw new \LogicException(__METHOD__ . ' is not implemented in ' . get_called_class());
}
/**
* Gets the map array.
*
* @return array
*/
public function getMapArray()
{
return static::$map;
}
/**
* Sorts the map.
*
* @return $this
*/
public function sort()
{
foreach (array_keys(static::$map) as $k) {
ksort(static::$map[$k]);
foreach (static::$map[$k] as &$sub) {
ksort($sub);
}
}
return $this;
}
/**
* Gets a list of entries of the map.
*
* @param string $entry
* The main array entry.
* @param string $match
* (Optional) a match wildcard to limit the list.
*
* @return array
* The list of the entries.
*/
protected function listEntries($entry, $match = null)
{
if (!isset(static::$map[$entry])) {
return [];
}
$list = array_keys(static::$map[$entry]);
if (is_null($match)) {
return $list;
} else {
$re = strtr($match, ['/' => '\\/', '*' => '.*']);
return array_filter($list, function ($v) use ($re) {
return preg_match("/$re/", $v) === 1;
});
}
}
/**
* Gets the content of an entry of the map.
*
* @param string $entry
* The main array entry.
* @param string $entry_key
* The main entry value.
*
* @return mixed|null
* The value of the entry, or null if missing.
*/
protected function getMapEntry($entry, $entry_key)
{
return isset(static::$map[$entry][$entry_key]) ? static::$map[$entry][$entry_key] : null;
}
/**
* Gets the content of a subentry of the map.
*
* @param string $entry
* The main array entry.
* @param string $entry_key
* The main entry value.
* @param string $sub_entry
* The sub entry.
*
* @return mixed|null
* The value of the entry, or null if missing.
*/
protected function getMapSubEntry($entry, $entry_key, $sub_entry)
{
return isset(static::$map[$entry][$entry_key][$sub_entry]) ? static::$map[$entry][$entry_key][$sub_entry] : null;
}
/**
* Adds an entry to the map.
*
* Checks that no duplicate entries are made.
*
* @param string $entry
* The main array entry.
* @param string $entry_key
* The main entry value.
* @param string $sub_entry
* The sub entry.
* @param string $value
* The value to add.
*
* @return $this
*/
protected function addMapSubEntry($entry, $entry_key, $sub_entry, $value)
{
if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
static::$map[$entry][$entry_key][$sub_entry] = [$value];
} else {
if (array_search($value, static::$map[$entry][$entry_key][$sub_entry]) === false) {
static::$map[$entry][$entry_key][$sub_entry][] = $value;
}
}
return $this;
}
/**
* Removes an entry from the map.
*
* @param string $entry
* The main array entry.
* @param string $entry_key
* The main entry value.
* @param string $sub_entry
* The sub entry.
* @param string $value
* The value to remove.
*
* @return bool
* true if the entry was removed, false if the entry was not present.
*/
protected function removeMapSubEntry($entry, $entry_key, $sub_entry, $value)
{
// Return false if no entry.
if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
return false;
}
// Return false if no value.
$k = array_search($value, static::$map[$entry][$entry_key][$sub_entry]);
if ($k === false) {
return false;
}
// Remove the map sub entry key.
unset(static::$map[$entry][$entry_key][$sub_entry][$k]);
// Remove the sub entry if no more values.
if (empty(static::$map[$entry][$entry_key][$sub_entry])) {
unset(static::$map[$entry][$entry_key][$sub_entry]);
} else {
// Resequence the remaining values.
$tmp = [];
foreach (static::$map[$entry][$entry_key][$sub_entry] as $v) {
$tmp[] = $v;
}
static::$map[$entry][$entry_key][$sub_entry] = $tmp;
}
// Remove the entry if no more values.
if (empty(static::$map[$entry][$entry_key])) {
unset(static::$map[$entry][$entry_key]);
}
return true;
}
/**
* Sets a value as the default for an entry.
*
* @param string $entry
* The main array entry.
* @param string $entry_key
* The main entry value.
* @param string $sub_entry
* The sub entry.
* @param string $value
* The value to add.
*
* @throws MappingException if no mapping found.
*
* @return $this
*/
protected function setValueAsDefault($entry, $entry_key, $sub_entry, $value)
{
// Throw exception if no entry.
if (!isset(static::$map[$entry][$entry_key][$sub_entry])) {
throw new MappingException("Cannot set '{$value}' as default for '{$entry_key}', '{$entry_key}' not defined");
}
// Throw exception if no entry-value pair.
$k = array_search($value, static::$map[$entry][$entry_key][$sub_entry]);
if ($k === false) {
throw new MappingException("Cannot set '{$value}' as default for '{$entry_key}', '{$value}' not associated to '{$entry_key}'");
}
// Move value to top of array and resequence the rest.
$tmp = [$value];
foreach (static::$map[$entry][$entry_key][$sub_entry] as $kk => $v) {
if ($kk === $k) {
continue;
}
$tmp[] = $v;
}
static::$map[$entry][$entry_key][$sub_entry] = $tmp;
return $this;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
<?php
namespace FileEye\MimeMap\Map;
/**
* Class for mapping file extensions to MIME types.
*
* This class has no mapping defined. It can be used for feeding updates and
* for testing purposes.
*/
class EmptyMap extends AbstractMap
{
/**
* Singleton instance.
*
* @var AbstractMap
*/
protected static $instance;
}

View File

@@ -0,0 +1,78 @@
<?php
namespace FileEye\MimeMap\Map;
/**
* Class for mapping file extensions to MIME types.
*
* This class has minimum mapping defined, and is by defualt not autoloadable
* (it has a .php.test extension). It is used for testing purposes.
*/
class MiniMap extends AbstractMap
{
/**
* Singleton instance.
*
* @var AbstractMap
*/
protected static $instance;
/**
* Returns this file's fully qualified filename.
*
* @return string
*/
public function getFileName()
{
return __FILE__;
}
/**
* Mapping between file extensions and MIME types.
*
* The array has three main keys, 't' that stores MIME types, 'e' that map
* file extensions to MIME types, and 'a' that store MIME type aliases.
*
* The entire map is created automatically by running
* $ fileye-mimemap update [URL] [YAML] [FILE]
* on the command line.
* The utility application fetches the map from the Apache HTTPD
* documentation website, and integrates its definitions with any further
* specifications contained in the YAML file.
*
* DO NOT CHANGE THE MAPPING ARRAY MANUALLY.
*
* @internal
*
* @var array
*/
// phpcs:disable
protected static $map = array (
't' =>
array (
'application/andrew-inset' =>
array (
'desc' =>
array (
0 => 'ATK inset',
1 => 'ATK: Andrew Toolkit',
),
'e' =>
array (
0 => 'ez',
),
),
),
'e' =>
array (
'ez' =>
array (
't' =>
array (
0 => 'application/andrew-inset',
),
),
),
);
// phpcs:enable
}

View File

@@ -0,0 +1,50 @@
<?php
namespace FileEye\MimeMap;
/**
* Class for managing map singletons.
*/
abstract class MapHandler
{
/**
* The default map PHP class.
*/
const DEFAULT_MAP_CLASS = '\FileEye\MimeMap\Map\DefaultMap';
/**
* The default map class to use.
*
* It can be overridden by ::setDefaultMapClass.
*
* @var string
*/
protected static $defaultMapClass = MapHandler::DEFAULT_MAP_CLASS;
/**
* Sets a map class as default for new instances.
*
* @param string $map_class A FQCN.
*/
public static function setDefaultMapClass($map_class)
{
static::$defaultMapClass = $map_class;
}
/**
* Returns the map instance.
*
* @param string $map_class
* (Optional) The map FQCN to be used. If null, the default map will be
* used.
*
* @return Map\AbstractMap
*/
public static function map($map_class = null)
{
if ($map_class === null) {
$map_class = static::$defaultMapClass;
}
return $map_class::getInstance();
}
}

View File

@@ -0,0 +1,212 @@
<?php
namespace FileEye\MimeMap;
use FileEye\MimeMap\Map\AbstractMap;
/**
* Compiles the MIME type to file extension map.
*/
class MapUpdater
{
/**
* The default, empty, base map to use for update.
*/
const DEFAULT_BASE_MAP_CLASS = '\FileEye\MimeMap\Map\EmptyMap';
/**
* The AbstractMap object to update.
*
* @var AbstractMap
*/
protected $map;
/**
* Returns the default file with override commands to be executed.
*
* The YAML file provides an array of calls to MapHandler methods to be
* executed sequentially. Each entry indicates the method to be invoked and
* the arguments to be passed in.
*
* @return string
*/
public static function getDefaultMapBuildFile()
{
return __DIR__ . '/../resources/default_map_build.yml';
}
/**
* Returns the AbstractMap object being updated.
*
* @return AbstractMap
*/
public function getMap()
{
return $this->map;
}
/**
* Sets the AbstractMap object to update.
*
* @param string $map_class
* The FQCN of the map to be updated.
*
* @return $this
*/
public function selectBaseMap($map_class)
{
$this->map = MapHandler::map($map_class);
$this->map->backup();
return $this;
}
/**
* Loads a new type-to-extension map reading from a file in Apache format.
*
* @param string $source_file
* The source file. The file must conform to the format in the Apache
* source code repository file where MIME types and file extensions are
* associated.
*
* @return string[]
* An array of error messages.
*
* @throws \RuntimeException
* If it was not possible to access the file.
*/
public function loadMapFromApacheFile($source_file)
{
$errors = [];
$lines = @file($source_file);
if ($lines === false) {
throw new \RuntimeException("Failed accessing {$source_file}");
}
foreach ($lines as $line) {
if ($line[0] == '#') {
continue;
}
$line = preg_replace("#\\s+#", ' ', trim($line));
$parts = explode(' ', $line);
$type = array_shift($parts);
foreach ($parts as $extension) {
$this->map->addTypeExtensionMapping($type, $extension);
}
}
$this->map->sort();
return $errors;
}
/**
* Loads a new type-to-extension map reading from a Freedesktop.org file.
*
* @param string $source_file
* The source file. The file must conform to the format in the
* Freedesktop.org database.
*
* @return string[]
* An array of error messages.
*/
public function loadMapFromFreedesktopFile($source_file)
{
$errors = [];
$xml = simplexml_load_string(file_get_contents($source_file));
$aliases = [];
foreach ($xml as $node) {
$exts = [];
foreach ($node->glob as $glob) {
$pattern = (string) $glob['pattern'];
if ('*' != $pattern[0] || '.' != $pattern[1]) {
continue;
}
$exts[] = substr($pattern, 2);
}
if (empty($exts)) {
continue;
}
$type = (string) $node['type'];
// Add description.
if (isset($node->comment)) {
$this->map->addTypeDescription($type, (string) $node->comment[0]);
}
if (isset($node->acronym)) {
$acronym = (string) $node->acronym;
if (isset($node->{'expanded-acronym'})) {
$acronym .= ': ' . (string) $node->{'expanded-acronym'};
}
$this->map->addTypeDescription($type, $acronym);
}
// Add extensions.
foreach ($exts as $ext) {
$this->map->addTypeExtensionMapping($type, $ext);
}
// All aliases are accumulated and processed at the end of the
// cycle to allow proper consistency checking on the completely
// developed list of types.
foreach ($node->alias as $alias) {
$aliases[$type][] = (string) $alias['type'];
}
}
// Add all the aliases, provide logging of errors.
foreach ($aliases as $type => $a) {
foreach ($a as $alias) {
try {
$this->map->addTypeAlias($type, $alias);
} catch (MappingException $e) {
$errors[] = $e->getMessage();
}
}
}
$this->map->sort();
return $errors;
}
/**
* Applies to the map an array of overrides.
*
* @param array $overrides
* The overrides to be applied.
*
* @return string[]
* An array of error messages.
*/
public function applyOverrides(array $overrides)
{
$errors = [];
foreach ($overrides as $command) {
try {
call_user_func_array([$this->map, $command[0]], $command[1]);
} catch (MappingException $e) {
$errors[] = $e->getMessage();
}
}
$this->map->sort();
return $errors;
}
/**
* Updates the map at a destination PHP file.
*
* @return $this
*/
public function writeMapToPhpClassFile($file)
{
$content = preg_replace(
'#protected static \$map = (.+?);#s',
"protected static \$map = " . preg_replace('/\s+$/m', '', var_export($this->map->getMapArray(), true)) . ";",
file_get_contents($file)
);
file_put_contents($file, $content);
return $this;
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace FileEye\MimeMap;
/**
* Exception thrown for MIME type to file extension mapping inconsistencies.
*/
class MappingException extends \RuntimeException
{
}

View File

@@ -0,0 +1,538 @@
<?php
namespace FileEye\MimeMap;
/**
* Class for working with MIME types
*/
class Type
{
/**
* Short format [e.g. image/jpeg] for strings.
*/
const SHORT_TEXT = 0;
/**
* Full format [e.g. image/jpeg; p="1"] for strings.
*/
const FULL_TEXT = 1;
/**
* Full format with comments [e.g. image/jpeg; p="1" (comment)] for strings.
*/
const FULL_TEXT_WITH_COMMENTS = 2;
/**
* The MIME media type.
*
* @var string
*/
protected $media;
/**
* The MIME media type comment.
*
* @var string
*/
protected $mediaComment;
/**
* The MIME media sub-type.
*
* @var string
*/
protected $subType;
/**
* The MIME media sub-type comment.
*
* @var string
*/
protected $subTypeComment;
/**
* Optional MIME parameters.
*
* @var TypeParameter[]
*/
protected $parameters = [];
/**
* The MIME types map.
*
* @var Map\AbstractMap
*/
protected $map;
/**
* Constructor.
*
* The type string will be parsed and the appropriate class vars set.
*
* @param string $type_string
* (Optional) MIME type string to be parsed.
* @param string $map_class
* (Optional) The FQCN of the map class to use.
*/
public function __construct($type_string = null, $map_class = null)
{
if ($type_string !== null) {
TypeParser::parse($type_string, $this);
}
$this->map = MapHandler::map($map_class);
}
/**
* Gets a MIME type's media.
*
* Note: 'media' refers to the portion before the first slash.
*
* @return string
* Type's media.
*/
public function getMedia()
{
return $this->media;
}
/**
* Sets a MIME type's media.
*
* @param string $media
* Type's media.
*
* @return $this
*/
public function setMedia($media)
{
$this->media = $media;
return $this;
}
/**
* Gets a MIME type's media comment.
*
* @return string
* Type's media comment.
*/
public function getMediaComment()
{
return $this->mediaComment;
}
/**
* Sets a MIME type's media comment.
*
* @param string $comment
* Type's media comment.
*
* @return $this
*/
public function setMediaComment($comment)
{
$this->mediaComment = $comment;
return $this;
}
/**
* Gets a MIME type's subtype.
*
* @return string
* Type's subtype, null if invalid mime type.
*/
public function getSubType()
{
return $this->subType;
}
/**
* Sets a MIME type's subtype.
*
* @param string $sub_type
* Type's subtype.
*
* @return $this
*/
public function setSubType($sub_type)
{
$this->subType = $sub_type;
return $this;
}
/**
* Gets a MIME type's subtype comment.
*
* @return string
* Type's subtype comment, null if invalid mime type.
*/
public function getSubTypeComment()
{
return $this->subTypeComment;
}
/**
* Sets a MIME type's subtype comment.
*
* @param string $comment
* Type's subtype comment.
*
* @return $this
*/
public function setSubTypeComment($comment)
{
$this->subTypeComment = $comment;
return $this;
}
/**
* Does this type have any parameters?
*
* @return boolean
* True if type has parameters, false otherwise.
*/
public function hasParameters()
{
return (bool) $this->parameters;
}
/**
* Get a MIME type's parameters.
*
* @return TypeParameter[]
*/
public function getParameters()
{
return $this->parameters;
}
/**
* Get a MIME type's parameter.
*
* @param string $name
* Parameter name
*
* @return TypeParameter|null
*/
public function getParameter($name)
{
return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
}
/**
* Add a parameter to this type
*
* @param string $name
* Parameter name.
* @param string $value
* Parameter value.
* @param string $comment
* Comment for this parameter.
*
* @return void
*/
public function addParameter($name, $value, $comment = null)
{
$this->parameters[$name] = new TypeParameter($name, $value, $comment);
}
/**
* Remove a parameter from this type.
*
* @param string $name
* Parameter name.
*
* @return void
*/
public function removeParameter($name)
{
unset($this->parameters[$name]);
}
/**
* Create a textual MIME type from object values.
*
* This function performs the opposite function of parse().
*
* @param int $format
* The format of the output string.
*
* @return string
* MIME type string.
*/
public function toString($format = Type::FULL_TEXT)
{
if (!isset($this->media) || !isset($this->subType)) {
return null;
}
$type = strtolower($this->media);
if ($format > Type::FULL_TEXT && isset($this->mediaComment)) {
$type .= ' (' . $this->mediaComment . ')';
}
$type .= '/' . strtolower($this->subType);
if ($format > Type::FULL_TEXT && isset($this->subTypeComment)) {
$type .= ' (' . $this->subTypeComment . ')';
}
if ($format > Type::SHORT_TEXT && count($this->parameters)) {
foreach ($this->parameters as $parameter) {
$type .= '; ' . $parameter->toString($format);
}
}
return $type;
}
/**
* Is this type experimental?
*
* Note: Experimental types are denoted by a leading 'x-' in the media or
* subtype, e.g. text/x-vcard or x-world/x-vrml.
*
* @return boolean
* True if type is experimental, false otherwise.
*/
public function isExperimental()
{
return substr($this->getMedia(), 0, 2) == 'x-' || substr($this->getSubType(), 0, 2) == 'x-';
}
/**
* Is this a vendor MIME type?
*
* Note: Vendor types are denoted with a leading 'vnd. in the subtype.
*
* @return boolean
* True if type is a vendor type, false otherwise.
*/
public function isVendor()
{
return substr($this->getSubType(), 0, 4) == 'vnd.';
}
/**
* Is this a wildcard type?
*
* @return boolean
* True if type is a wildcard, false otherwise.
*/
public function isWildcard()
{
return ($this->getMedia() === '*' && $this->getSubtype() === '*') || strpos($this->getSubtype(), '*') !== false;
}
/**
* Is this an alias?
*
* @return boolean
* True if type is an alias, false otherwise.
*/
public function isAlias()
{
return $this->map->hasAlias($this->toString(static::SHORT_TEXT));
}
/**
* Perform a wildcard match on a MIME type
*
* Example:
* $type = new Type('image/png');
* $type->wildcardMatch('image/*');
*
* @param string $wildcard
* Wildcard to check against.
*
* @return boolean
* True if there was a match, false otherwise.
*/
public function wildcardMatch($wildcard)
{
$wildcard_type = new static($wildcard);
if (!$wildcard_type->isWildcard()) {
return false;
}
$wildcard_re = strtr($wildcard_type->toString(static::SHORT_TEXT), [
'/' => '\\/',
'*' => '.*',
]);
$subject = $this->toString(static::SHORT_TEXT);
return preg_match("/$wildcard_re/", $subject) === 1;
}
/**
* Builds a list of MIME types existing in the map.
*
* If the current type is a wildcard, than all the types matching the
* wildcard will be returned.
*
* @param bool $strict
* (Optional) if true a MappingException is thrown when no type is
* found, if false it returns an empty array as a default.
* Defaults to true.
*
* @throws MappingException if no mapping found and $strict is true.
*
* @return string[]
*/
protected function buildTypesList($strict = true)
{
$subject = $this->toString(static::SHORT_TEXT);
// Find all types.
$types = [];
if (!$this->isWildcard()) {
if ($this->map->hasType($subject)) {
$types[] = $subject;
}
} else {
foreach ($this->map->listTypes($subject) as $t) {
$types[] = $t;
}
}
// No types found, throw exception or return emtpy array.
if (empty($types)) {
if ($strict) {
throw new MappingException('No MIME type found for ' . $subject . ' in map');
} else {
return [];
}
}
return $types;
}
/**
* Returns the unaliased MIME type.
*
* @return Type
* $this if the current type is not an alias, the parent type if the
* current type is an alias.
*/
protected function getUnaliasedType()
{
return $this->isAlias() ? new static($this->map->getAliasTypes($this->toString(static::SHORT_TEXT))[0]) : $this;
}
/**
* Returns a description for the MIME type, if existing in the map.
*
* @param bool $include_acronym
* (Optional) if true and an acronym description exists for the type,
* the returned description will contain the acronym and its description,
* appended with a comma. Defaults to false.
*
* @return string|null
*/
public function getDescription($include_acronym = false)
{
$descriptions = $this->map->getTypeDescriptions($this->getUnaliasedType()->toString(static::SHORT_TEXT));
$res = null;
if (isset($descriptions[0])) {
$res = $descriptions[0];
}
if ($include_acronym && isset($descriptions[1])) {
$res .= ', ' . $descriptions[1];
}
return $res;
}
/**
* Returns all the aliases related to the MIME type(s).
*
* If the current type is a wildcard, than all aliases of all the
* types matching the wildcard will be returned.
*
* @param bool $strict
* (Optional) if true a MappingException is thrown when no mapping is
* found, if false it returns an empty array as a default.
* Defaults to true.
*
* @throws MappingException if error and $strict is true.
*
* @return string[]
*/
public function getAliases($strict = true)
{
// Fail if the current type is an alias already.
if ($this->isAlias()) {
if ($strict) {
$subject = $this->toString(static::SHORT_TEXT);
throw new MappingException("Cannot get aliases for '{$subject}', it is an alias itself");
} else {
return [];
}
}
// Build the array of aliases.
$aliases = [];
foreach ($this->buildTypesList($strict) as $t) {
foreach ($this->map->getTypeAliases($t) as $a) {
$aliases[$a] = $a;
}
}
return array_keys($aliases);
}
/**
* Returns the MIME type's preferred file extension.
*
* @param bool $strict
* (Optional) if true a MappingException is thrown when no mapping is
* found, if false it returns null as a default.
* Defaults to true.
*
* @throws MappingException if no mapping found and $strict is true.
*
* @return string
*/
public function getDefaultExtension($strict = true)
{
$unaliased_type = $this->getUnaliasedType();
$subject = $unaliased_type->toString(static::SHORT_TEXT);
if (!$unaliased_type->isWildcard()) {
$proceed = $this->map->hasType($subject);
} else {
$proceed = count($this->map->listTypes($subject)) === 1;
}
if (!$proceed) {
if ($strict) {
throw new MappingException('Cannot determine default extension for type: ' . $unaliased_type->toString(static::SHORT_TEXT));
} else {
return null;
}
}
return $unaliased_type->getExtensions()[0];
}
/**
* Returns all the file extensions related to the MIME type(s).
*
* If the current type is a wildcard, than all extensions of all the
* types matching the wildcard will be returned.
*
* @param bool $strict
* (Optional) if true a MappingException is thrown when no mapping is
* found, if false it returns an empty array as a default.
* Defaults to true.
*
* @throws MappingException if no mapping found and $strict is true.
*
* @return string[]
*/
public function getExtensions($strict = true)
{
// Build the array of extensions.
$extensions = [];
foreach ($this->getUnaliasedType()->buildTypesList($strict) as $t) {
foreach ($this->map->getTypeExtensions($t) as $e) {
$extensions[$e] = $e;
}
}
return array_keys($extensions);
}
}

View File

@@ -0,0 +1,100 @@
<?php
namespace FileEye\MimeMap;
/**
* Class for working with MIME type parameters.
*/
class TypeParameter
{
/**
* Parameter name.
*
* @var string
*/
protected $name;
/**
* Parameter value.
*
* @var string
*/
protected $value;
/**
* Parameter comment.
*
* @var string
*/
protected $comment;
/**
* Constructor.
*
* @param string $name Parameter name.
* @param string $value Parameter value.
* @param string $comment Comment for this parameter.
*/
public function __construct($name, $value, $comment = null)
{
$this->name = $name;
$this->value = $value;
$this->comment = $comment;
}
/**
* Gets the parameter name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Gets the parameter value.
*
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* Does this parameter have a comment?
*
* @return boolean true if param has a comment, false otherwise.
*/
public function hasComment()
{
return (bool) $this->comment;
}
/**
* Gets the parameter comment.
*
* @return string
*/
public function getComment()
{
return $this->comment;
}
/**
* Gets a string representation of this parameter.
*
* @param int $format The format of the output string.
*
* @return string String representation of parameter.
*/
public function toString($format = Type::FULL_TEXT)
{
$val = $this->name . '="' . str_replace('"', '\\"', $this->value) . '"';
if ($format > Type::FULL_TEXT && $this->comment) {
$val .= ' (' . $this->comment . ')';
}
return $val;
}
}

View File

@@ -0,0 +1,154 @@
<?php
namespace FileEye\MimeMap;
/**
* Class for parsing RFC 2045 Content-Type Header Fields.
*/
class TypeParser
{
/**
* Parse a mime-type and set the class variables.
*
* @param string $type_string
* MIME type string to parse.
* @param Type $type
* The Type object to receive the components.
*
* @throws MalformedTypeException when $type_string is malformed.
*
* @return void
*/
public static function parse($type_string, Type $type)
{
// Media and SubType are separated by a slash '/'.
$media = static::parseStringPart($type_string, 0, '/');
if (!$media['string']) {
throw new MalformedTypeException('Media type not found');
}
if (!$media['delimiter_matched']) {
throw new MalformedTypeException('Slash \'/\' to separate media type and subtype not found');
}
$type->setMedia(strtolower((string) $media['string']));
$type->setMediaComment($media['comment']);
// SubType and Parameters are separated by semicolons ';'.
$sub = static::parseStringPart($type_string, $media['end_offset'] + 1, ';');
if (!$sub['string']) {
throw new MalformedTypeException('Media subtype not found');
}
$type->setSubType(strtolower((string) $sub['string']));
$type->setSubTypeComment($sub['comment']);
// Loops through the parameter.
while ($sub['delimiter_matched']) {
$sub = static::parseStringPart($type_string, $sub['end_offset'] + 1, ';');
$tmp = explode('=', $sub['string'], 2);
$p_name = trim($tmp[0]);
$p_val = trim($tmp[1]);
$p_val = str_replace('\\"', '"', $p_val);
$type->addParameter($p_name, $p_val, $sub['comment']);
}
}
/**
* Parses a part of the content MIME type string.
*
* Splits string and comment until a delimiter is found.
*
* @param string $string
* Input string.
* @param int $offset
* Offset to start parsing from.
* @param string $delimiter
* Stop parsing when delimiter found.
*
* @return array
* An array with the following keys:
* 'string' - the uncommented part of $string
* 'comment' - the comment part of $string
* 'delimiter_matched' - true if a $delimiter stopped the parsing, false
* otherwise
* 'end_offset' - the last position parsed in $string.
*/
public static function parseStringPart($string, $offset, $delimiter)
{
$inquote = false;
$escaped = false;
$incomment = 0;
$newstring = '';
$comment = '';
for ($n = $offset; $n < strlen($string); $n++) {
if ($string[$n] === $delimiter && !$escaped && !$inquote && $incomment === 0) {
break;
}
if ($escaped) {
if ($incomment == 0) {
$newstring .= $string[$n];
} else {
$comment .= $string[$n];
}
$escaped = false;
continue;
}
if ($string[$n] == '\\') {
if ($incomment > 0) {
$comment .= $string[$n];
}
$escaped = true;
continue;
}
if (!$inquote && $incomment > 0 && $string[$n] == ')') {
$incomment--;
if ($incomment == 0) {
$comment .= ' ';
}
continue;
}
if (!$inquote && $string[$n] == '(') {
$incomment++;
continue;
}
if ($string[$n] == '"') {
if ($incomment > 0) {
$comment .= $string[$n];
} else {
if ($inquote) {
$inquote = false;
} else {
$inquote = true;
}
}
continue;
}
if ($incomment == 0) {
$newstring .= $string[$n];
continue;
}
$comment .= $string[$n];
}
if ($incomment > 0) {
throw new MalformedTypeException('Comment closing bracket missing: ' . $comment);
}
return [
'string' => empty($newstring) ? null : trim($newstring),
'comment' => empty($comment) ? null : trim($comment),
'delimiter_matched' => isset($string[$n]) ? ($string[$n] === $delimiter) : false,
'end_offset' => $n,
];
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace FileEye\MimeMap;
use Composer\InstalledVersions;
/**
* MimeMap version.
*/
class Version
{
/**
* Returns the current version of MimeMap.
*
* @return string The current version of MimeMap.
*/
public static function get()
{
return InstalledVersions::getPrettyVersion('fileeye/mimemap');
}
}

View File

@@ -0,0 +1,275 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mime-info [
<!ELEMENT mime-info (mime-type)+>
<!ATTLIST mime-info xmlns CDATA #FIXED "http://www.freedesktop.org/standards/shared-mime-info">
<!ELEMENT mime-type (comment+, (acronym,expanded-acronym)? , (icon? | generic-icon? | glob | magic | treemagic | root-XML | alias | sub-class-of)*)>
<!ATTLIST mime-type type CDATA #REQUIRED>
<!-- a comment describing a document with the respective MIME type. Example: "WMV video" -->
<!ELEMENT comment (#PCDATA)>
<!ATTLIST comment xml:lang CDATA #IMPLIED>
<!-- a comment describing the respective unexpanded MIME type acronym. Example: "WMV" -->
<!ELEMENT acronym (#PCDATA)>
<!ATTLIST acronym xml:lang CDATA #IMPLIED>
<!-- a comment describing the respective expanded MIME type acronym. Example: "Windows Media Video" -->
<!ELEMENT expanded-acronym (#PCDATA)>
<!ATTLIST expanded-acronym xml:lang CDATA #IMPLIED>
<!ELEMENT icon EMPTY>
<!ATTLIST icon name CDATA #REQUIRED>
<!-- a generic icon name as per the Icon Naming Specification, only required if computing
it from the mime-type would not work, See "generic-icon" in the Shared Mime Specification -->
<!ELEMENT generic-icon EMPTY>
<!ATTLIST generic-icon name (application-x-executable|audio-x-generic|folder|font-x-generic|image-x-generic|package-x-generic|text-html|text-x-generic|text-x-generic-template|text-x-script|video-x-generic|x-office-address-book|x-office-calendar|x-office-document|x-office-presentation|x-office-spreadsheet) #REQUIRED>
<!ELEMENT glob EMPTY>
<!ATTLIST glob pattern CDATA #REQUIRED>
<!ATTLIST glob weight CDATA "50">
<!ATTLIST glob case-sensitive CDATA #IMPLIED>
<!ELEMENT magic (match)+>
<!ATTLIST magic priority CDATA "50">
<!ELEMENT match (match)*>
<!ATTLIST match offset CDATA #REQUIRED>
<!ATTLIST match type (string|big16|big32|little16|little32|host16|host32|byte) #REQUIRED>
<!ATTLIST match value CDATA #REQUIRED>
<!ATTLIST match mask CDATA #IMPLIED>
<!ELEMENT treemagic (treematch)+>
<!ATTLIST treemagic priority CDATA "50">
<!ELEMENT treematch (treematch)*>
<!ATTLIST treematch path CDATA #REQUIRED>
<!ATTLIST treematch type (file|directory|link) #IMPLIED>
<!ATTLIST treematch match-case (true|false) #IMPLIED>
<!ATTLIST treematch executable (true|false) #IMPLIED>
<!ATTLIST treematch non-empty (true|false) #IMPLIED>
<!ATTLIST treematch mimetype CDATA #IMPLIED>
<!ELEMENT root-XML EMPTY>
<!ATTLIST root-XML namespaceURI CDATA #REQUIRED>
<!ATTLIST root-XML localName CDATA #REQUIRED>
<!ELEMENT alias EMPTY>
<!ATTLIST alias type CDATA #REQUIRED>
<!ELEMENT sub-class-of EMPTY>
<!ATTLIST sub-class-of type CDATA #REQUIRED>
]>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-atari-2600-rom">
<comment>Atari 2600</comment>
<comment xml:lang="ast">Atari 2600</comment>
<comment xml:lang="ca">Atari 2600</comment>
<comment xml:lang="cs">Atari 2600</comment>
<comment xml:lang="da">Atari 2600</comment>
<comment xml:lang="de">Atari 2600</comment>
<comment xml:lang="en_GB">Atari 2600</comment>
<comment xml:lang="es">Atari 2600</comment>
<comment xml:lang="eu">Atari 2600</comment>
<comment xml:lang="fi">Atari 2600</comment>
<comment xml:lang="fr">Atari 2600</comment>
<comment xml:lang="ga">Atari 2600</comment>
<comment xml:lang="he">אטארי 2600</comment>
<comment xml:lang="hr">Atari 2600</comment>
<comment xml:lang="hu">Atari 2600</comment>
<comment xml:lang="id">Atari 2600</comment>
<comment xml:lang="it">Atari 2600</comment>
<comment xml:lang="kk">Atari 2600</comment>
<comment xml:lang="ko">Atari 2600</comment>
<comment xml:lang="oc">Atari 2600</comment>
<comment xml:lang="pl">Atari 2600</comment>
<comment xml:lang="pt_BR">Atari 2600</comment>
<comment xml:lang="ru">Atari 2600</comment>
<comment xml:lang="sk">Atari 2600</comment>
<comment xml:lang="sr">Атари 2600</comment>
<comment xml:lang="sv">Atari 2600</comment>
<comment xml:lang="tr">Atari 2600</comment>
<comment xml:lang="uk">Atari 2600</comment>
<comment xml:lang="zh_CN">雅达利 2600</comment>
<comment xml:lang="zh_TW">Atari 2600</comment>
<generic-icon name="application-x-executable"/>
<glob pattern="*.a26"/>
</mime-type>
<mime-type type="text/htmlh">
<comment>help page</comment>
<comment xml:lang="ar">صفحة المساعدة</comment>
<comment xml:lang="az">yardım səhifəsi</comment>
<comment xml:lang="be@latin">staronka dapamohi</comment>
<comment xml:lang="bg">Страница от помощта</comment>
<comment xml:lang="ca">pàgina d'ajuda</comment>
<comment xml:lang="cs">stránka nápovědy</comment>
<comment xml:lang="cy">tudalen gymorth</comment>
<comment xml:lang="da">hjælpeside</comment>
<comment xml:lang="de">Hilfeseite</comment>
<comment xml:lang="el">Σελίδα βοήθειας</comment>
<comment xml:lang="en_GB">help page</comment>
<comment xml:lang="eo">help-paĝo</comment>
<comment xml:lang="es">página de ayuda</comment>
<comment xml:lang="eu">laguntzako orria</comment>
<comment xml:lang="fi">ohjesivu</comment>
<comment xml:lang="fo">hjálparsíða</comment>
<comment xml:lang="fr">page d'aide</comment>
<comment xml:lang="ga">leathanach cabhrach</comment>
<comment xml:lang="gl">páxina de axuda</comment>
<comment xml:lang="he">דף עזרה</comment>
<comment xml:lang="hr">Stranica pomoći</comment>
<comment xml:lang="hu">súgóoldal</comment>
<comment xml:lang="ia">Pagina de adjuta</comment>
<comment xml:lang="id">halaman bantuan</comment>
<comment xml:lang="it">Pagina di aiuto</comment>
<comment xml:lang="ja">ヘルプページ</comment>
<comment xml:lang="kk">анықтама парағы</comment>
<comment xml:lang="ko">도움말 페이지</comment>
<comment xml:lang="lt">žinyno puslapis</comment>
<comment xml:lang="lv">palīdzības lapa</comment>
<comment xml:lang="ms">Halaman bantuan</comment>
<comment xml:lang="nb">hjelpside</comment>
<comment xml:lang="nl">hulppagina</comment>
<comment xml:lang="nn">hjelpeside</comment>
<comment xml:lang="oc">pagina d'ajuda</comment>
<comment xml:lang="pl">Strona pomocy</comment>
<comment xml:lang="pt">página de ajuda</comment>
<comment xml:lang="pt_BR">Página de ajuda</comment>
<comment xml:lang="ro">pagină de ajutor</comment>
<comment xml:lang="ru">Страница справки</comment>
<comment xml:lang="sk">Stránka Pomocníka</comment>
<comment xml:lang="sl">stran pomoči</comment>
<comment xml:lang="sq">Faqe ndihme</comment>
<comment xml:lang="sr">страница помоћи</comment>
<comment xml:lang="sv">hjälpsida</comment>
<comment xml:lang="tr">yardım sayfası</comment>
<comment xml:lang="uk">сторінка довідки</comment>
<comment xml:lang="vi">trang trợ giúp</comment>
<comment xml:lang="zh_CN">帮助页面</comment>
<comment xml:lang="zh_TW">求助頁面</comment>
<sub-class-of type="text/plain"/>
</mime-type>
<mime-type type="text/plain">
<comment>plain text document</comment>
<comment xml:lang="ar">مستند نصي مجرد</comment>
<comment xml:lang="ast">documentu de testu planu</comment>
<comment xml:lang="be@latin">prosty tekstavy dakument</comment>
<comment xml:lang="bg">Документ с неформатиран текст</comment>
<comment xml:lang="ca">document de text pla</comment>
<comment xml:lang="cs">prostý textový dokument</comment>
<comment xml:lang="da">rent tekstdokument</comment>
<comment xml:lang="de">Einfaches Textdokument</comment>
<comment xml:lang="el">Έγγραφο απλού κειμένου</comment>
<comment xml:lang="en_GB">plain text document</comment>
<comment xml:lang="eo">plata teksta dokumento</comment>
<comment xml:lang="es">documento de texto sencillo</comment>
<comment xml:lang="eu">testu soileko dokumentua</comment>
<comment xml:lang="fi">perustekstiasiakirja</comment>
<comment xml:lang="fr">document texte brut</comment>
<comment xml:lang="ga">cáipéis ghnáth-théacs</comment>
<comment xml:lang="gl">documento de texto sinxelo</comment>
<comment xml:lang="he">מסמך טקסט פשוט</comment>
<comment xml:lang="hr">Običan tekstovni dokument</comment>
<comment xml:lang="hu">egyszerű szöveg</comment>
<comment xml:lang="ia">Documento de texto simple</comment>
<comment xml:lang="id">dokumen teks polos</comment>
<comment xml:lang="it">Documento in testo semplice</comment>
<comment xml:lang="ja">平文テキストドキュメント</comment>
<comment xml:lang="kk">мәтіндік құжаты</comment>
<comment xml:lang="ko">일반 텍스트 문서</comment>
<comment xml:lang="lt">paprastas tekstinis dokumentas</comment>
<comment xml:lang="lv">vienkāršs teksta dokuments</comment>
<comment xml:lang="ms">Dokumen teks jernih</comment>
<comment xml:lang="nb">vanlig tekstdokument</comment>
<comment xml:lang="nl">plattetekst-document</comment>
<comment xml:lang="nn">vanleg tekstdokument</comment>
<comment xml:lang="oc">document tèxte brut</comment>
<comment xml:lang="pl">Zwykły dokument tekstowy</comment>
<comment xml:lang="pt">documento em texto simples</comment>
<comment xml:lang="pt_BR">Documento de Texto</comment>
<comment xml:lang="ro">document text simplu</comment>
<comment xml:lang="ru">Текстовый документ</comment>
<comment xml:lang="sk">Obyčajný textový dokument</comment>
<comment xml:lang="sl">običajna besedilna datoteka</comment>
<comment xml:lang="sq">Dokument në tekst të thjeshtë</comment>
<comment xml:lang="sr">обичан текстуални документ</comment>
<comment xml:lang="sv">vanligt textdokument</comment>
<comment xml:lang="tr">düz metin belgesi</comment>
<comment xml:lang="uk">звичайний текстовий документ</comment>
<comment xml:lang="vi">tài liệu nhập thô</comment>
<comment xml:lang="zh_CN">纯文本文档</comment>
<comment xml:lang="zh_TW">純文字文件</comment>
<magic priority="50">
<match value="This is TeX," type="string" offset="0"/>
<match value="This is METAFONT," type="string" offset="0"/>
</magic>
<glob pattern="*.txt"/>
<glob pattern="*.asc"/>
<glob pattern="*,v"/>
</mime-type>
<mime-type type="application/pdf">
<comment>PDF document</comment>
<comment xml:lang="ar">مستند PDF</comment>
<comment xml:lang="ast">Documentu PDF</comment>
<comment xml:lang="be@latin">Dakument PDF</comment>
<comment xml:lang="bg">Документ — PDF</comment>
<comment xml:lang="ca">document PDF</comment>
<comment xml:lang="cs">dokument PDF</comment>
<comment xml:lang="cy">Dogfen PDF</comment>
<comment xml:lang="da">PDF-dokument</comment>
<comment xml:lang="de">PDF-Dokument</comment>
<comment xml:lang="el">Έγγραφο PDF</comment>
<comment xml:lang="en_GB">PDF document</comment>
<comment xml:lang="eo">PDF-dokumento</comment>
<comment xml:lang="es">documento PDF</comment>
<comment xml:lang="eu">PDF dokumentua</comment>
<comment xml:lang="fi">PDF-asiakirja</comment>
<comment xml:lang="fo">PDF skjal</comment>
<comment xml:lang="fr">document PDF</comment>
<comment xml:lang="ga">cáipéis PDF</comment>
<comment xml:lang="gl">documento PDF</comment>
<comment xml:lang="he">מסמך PDF</comment>
<comment xml:lang="hr">PDF dokument</comment>
<comment xml:lang="hu">PDF-dokumentum</comment>
<comment xml:lang="ia">Documento PDF</comment>
<comment xml:lang="id">Dokumen PDF</comment>
<comment xml:lang="it">Documento PDF</comment>
<comment xml:lang="ja">PDF ドキュメント</comment>
<comment xml:lang="kk">PDF құжаты</comment>
<comment xml:lang="ko">PDF 문서</comment>
<comment xml:lang="lt">PDF dokumentas</comment>
<comment xml:lang="lv">PDF dokuments</comment>
<comment xml:lang="ms">Dokumen PDF</comment>
<comment xml:lang="nb">PDF-dokument</comment>
<comment xml:lang="nl">PDF-document</comment>
<comment xml:lang="nn">PDF-dokument</comment>
<comment xml:lang="oc">document PDF</comment>
<comment xml:lang="pl">Dokument PDF</comment>
<comment xml:lang="pt">documento PDF</comment>
<comment xml:lang="pt_BR">Documento PDF</comment>
<comment xml:lang="ro">Document PDF</comment>
<comment xml:lang="ru">Документ PDF</comment>
<comment xml:lang="sk">Dokument PDF</comment>
<comment xml:lang="sl">Dokument PDF</comment>
<comment xml:lang="sq">Dokument PDF</comment>
<comment xml:lang="sr">ПДФ документ</comment>
<comment xml:lang="sv">PDF-dokument</comment>
<comment xml:lang="tr">PDF belgesi</comment>
<comment xml:lang="uk">документ PDF</comment>
<comment xml:lang="vi">Tài liệu PDF</comment>
<comment xml:lang="zh_CN">PDF 文档</comment>
<comment xml:lang="zh_TW">PDF 文件</comment>
<acronym>PDF</acronym>
<expanded-acronym>Portable Document Format</expanded-acronym>
<generic-icon name="x-office-document"/>
<magic priority="50">
<match value="%PDF-" type="string" offset="0:1024"/>
</magic>
<glob pattern="*.pdf"/>
<alias type="application/x-pdf"/>
<alias type="image/pdf"/>
<alias type="application/acrobat"/>
<alias type="application/nappdf"/>
</mime-type>
</mime-info>

View File

@@ -0,0 +1,8 @@
# This file maps Internet media types to unique file extension(s).
#
# This is a subset version for testing purposes.
#
# MIME type (lowercased) Extensions
# ============================================ ==========
image/jpeg jpeg jpg jpe
text/plain txt

View File

@@ -0,0 +1,22 @@
# This file maps Internet media types to unique file extension(s).
#
# This is a subset version for testing purposes.
# MIME type (lowercased) Extensions
# ============================================ ==========
# application/edi-consent
# application/edi-x12
# application/edifact
application/octet-stream bin dms lrf mar so dist distz pkg bpk dump elc deploy
application/pls+xml pls
audio/midi mid midi kar rmi
# audio/mobile-xmf
audio/mp4 m4a mp4a
image/jpeg jpeg jpg jpe
image/sgi sgi
image/svg+xml svg svgz
# image/t38
image/tiff tiff tif
text/css css
text/csv csv
text/plain txt text conf def list log in

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mime-info [
<!ELEMENT mime-info (mime-type)+>
<!ATTLIST mime-info xmlns CDATA #FIXED "http://www.freedesktop.org/standards/shared-mime-info">
<!ELEMENT mime-type (comment+, (acronym,expanded-acronym)? , (icon? | generic-icon? | glob | magic | treemagic | root-XML | alias | sub-class-of)*)>
<!ATTLIST mime-type type CDATA #REQUIRED>
<!-- a comment describing a document with the respective MIME type. Example: "WMV video" -->
<!ELEMENT comment (#PCDATA)>
<!ATTLIST comment xml:lang CDATA #IMPLIED>
<!-- a comment describing the respective unexpanded MIME type acronym. Example: "WMV" -->
<!ELEMENT acronym (#PCDATA)>
<!ATTLIST acronym xml:lang CDATA #IMPLIED>
<!-- a comment describing the respective expanded MIME type acronym. Example: "Windows Media Video" -->
<!ELEMENT expanded-acronym (#PCDATA)>
<!ATTLIST expanded-acronym xml:lang CDATA #IMPLIED>
<!ELEMENT icon EMPTY>
<!ATTLIST icon name CDATA #REQUIRED>
<!-- a generic icon name as per the Icon Naming Specification, only required if computing
it from the mime-type would not work, See "generic-icon" in the Shared Mime Specification -->
<!ELEMENT generic-icon EMPTY>
<!ATTLIST generic-icon name (application-x-executable|audio-x-generic|folder|font-x-generic|image-x-generic|package-x-generic|text-html|text-x-generic|text-x-generic-template|text-x-script|video-x-generic|x-office-address-book|x-office-calendar|x-office-document|x-office-presentation|x-office-spreadsheet) #REQUIRED>
<!ELEMENT glob EMPTY>
<!ATTLIST glob pattern CDATA #REQUIRED>
<!ATTLIST glob weight CDATA "50">
<!ATTLIST glob case-sensitive CDATA #IMPLIED>
<!ELEMENT magic (match)+>
<!ATTLIST magic priority CDATA "50">
<!ELEMENT match (match)*>
<!ATTLIST match offset CDATA #REQUIRED>
<!ATTLIST match type (string|big16|big32|little16|little32|host16|host32|byte) #REQUIRED>
<!ATTLIST match value CDATA #REQUIRED>
<!ATTLIST match mask CDATA #IMPLIED>
<!ELEMENT treemagic (treematch)+>
<!ATTLIST treemagic priority CDATA "50">
<!ELEMENT treematch (treematch)*>
<!ATTLIST treematch path CDATA #REQUIRED>
<!ATTLIST treematch type (file|directory|link) #IMPLIED>
<!ATTLIST treematch match-case (true|false) #IMPLIED>
<!ATTLIST treematch executable (true|false) #IMPLIED>
<!ATTLIST treematch non-empty (true|false) #IMPLIED>
<!ATTLIST treematch mimetype CDATA #IMPLIED>
<!ELEMENT root-XML EMPTY>
<!ATTLIST root-XML namespaceURI CDATA #REQUIRED>
<!ATTLIST root-XML localName CDATA #REQUIRED>
<!ELEMENT alias EMPTY>
<!ATTLIST alias type CDATA #REQUIRED>
<!ELEMENT sub-class-of EMPTY>
<!ATTLIST sub-class-of type CDATA #REQUIRED>
]>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
</mime-info>

View File

@@ -0,0 +1,45 @@
<?php
namespace FileEye\MimeMap\Test;
use FileEye\MimeMap\Extension;
use FileEye\MimeMap\MappingException;
class ExtensionTest extends MimeMapTestBase
{
public function testGetDefaultType()
{
$this->assertSame('text/plain', (new Extension('txt'))->getDefaultType());
$this->assertSame('text/plain', (new Extension('TXT'))->getDefaultType());
$this->assertSame('image/png', (new Extension('png'))->getDefaultType());
$this->assertSame('application/vnd.oasis.opendocument.text', (new Extension('odt'))->getDefaultType());
}
public function testGetStrictDefaultTypeUnknownExtension()
{
$this->expectException(MappingException::class);
$this->assertSame('application/octet-stream', (new Extension('ohmygodthatisnoextension'))->getDefaultType());
}
public function testGetNoStrictDefaultTypeUnknownExtension()
{
$this->assertSame('application/octet-stream', (new Extension('ohmygodthatisnoextension'))->getDefaultType(false));
}
public function testGetTypes()
{
$this->assertSame(['text/vnd.dvb.subtitle', 'image/vnd.dvb.subtitle', 'text/x-microdvd', 'text/x-mpsub', 'text/x-subviewer'], (new Extension('sub'))->getTypes());
$this->assertSame(['text/vnd.dvb.subtitle', 'image/vnd.dvb.subtitle', 'text/x-microdvd', 'text/x-mpsub', 'text/x-subviewer'], (new Extension('sUb'))->getTypes());
}
public function testGetStrictTypesUnknownExtension()
{
$this->expectException(MappingException::class);
$this->assertSame(['application/octet-stream'], (new Extension('ohmygodthatisnoextension'))->getTypes());
}
public function testGetNoStrictTypesUnknownExtension()
{
$this->assertSame(['application/octet-stream'], (new Extension('ohmygodthatisnoextension'))->getTypes(false));
}
}

View File

@@ -0,0 +1,301 @@
<?php
namespace FileEye\MimeMap\Test;
use FileEye\MimeMap\Extension;
use FileEye\MimeMap\MalformedTypeException;
use FileEye\MimeMap\MapHandler;
use FileEye\MimeMap\MappingException;
use FileEye\MimeMap\Type;
/**
* @backupStaticAttributes enabled
*/
class MapHandlerTest extends MimeMapTestBase
{
protected $map;
public function fcSetUp()
{
$this->map = MapHandler::map();
}
public function testMap()
{
$this->assertStringContainsString('DefaultMap.php', $this->map->getFileName());
}
public function testSort()
{
$this->map->addTypeExtensionMapping('aaa/aaa', '000a')->sort();
$this->assertSame('aaa/aaa', $this->map->listTypes()[0]);
$this->assertSame('000a', $this->map->listExtensions()[0]);
}
public function testAdd()
{
// Adding a new type with a new extension.
$this->map->addTypeExtensionMapping('bingo/bongo', 'bngbng');
$this->assertSame(['bngbng'], (new Type('bingo/bongo'))->getExtensions());
$this->assertSame('bngbng', (new Type('bingo/bongo'))->getDefaultExtension());
$this->assertSame(['bingo/bongo'], (new Extension('bngbng'))->getTypes());
$this->assertSame('bingo/bongo', (new Extension('bngbng'))->getDefaultType());
// Adding an already existing mapping should not duplicate entries.
$this->map->addTypeExtensionMapping('bingo/bongo', 'bngbng');
$this->assertSame(['bngbng'], (new Type('bingo/bongo'))->getExtensions());
$this->assertSame(['bingo/bongo'], (new Extension('bngbng'))->getTypes());
// Adding another extension to existing type.
$this->map->addTypeExtensionMapping('bingo/bongo', 'bigbog');
$this->assertSame(['bngbng', 'bigbog'], (new Type('bingo/bongo'))->getExtensions());
$this->assertSame(['bingo/bongo'], (new Extension('bigbog'))->getTypes());
// Adding another type to existing extension.
$this->map->addTypeExtensionMapping('boing/being', 'bngbng');
$this->assertSame(['bngbng'], (new Type('boing/being'))->getExtensions());
$this->assertSame(['bingo/bongo', 'boing/being'], (new Extension('bngbng'))->getTypes());
}
public function testRemove()
{
$this->assertSame(['txt', 'text', 'conf', 'def', 'list', 'log', 'in', 'asc'], (new Type('text/plain'))->getExtensions());
$this->assertSame('txt', (new Type('text/plain'))->getDefaultExtension());
$this->assertSame(['text/plain'], (new Extension('txt'))->getTypes());
$this->assertSame('text/plain', (new Extension('txt'))->getDefaultType());
// Remove an existing type-extension pair.
$this->assertTrue($this->map->removeTypeExtensionMapping('text/plain', 'txt'));
$this->assertSame(['text', 'conf', 'def', 'list', 'log', 'in', 'asc'], (new Type('text/plain'))->getExtensions());
$this->assertSame('text', (new Type('text/plain'))->getDefaultExtension());
$this->assertSame(['application/octet-stream'], (new Extension('txt'))->getTypes(false));
$this->assertSame('application/octet-stream', (new Extension('txt'))->getDefaultType(false));
// Try removing a non-existing extension.
$this->assertFalse($this->map->removeTypeExtensionMapping('text/plain', 'axx'));
// Remove an existing alias.
$this->assertSame(['application/x-pdf', 'image/pdf', 'application/acrobat', 'application/nappdf'], (new Type('application/pdf'))->getAliases());
$this->assertTrue($this->map->removeTypeAlias('application/pdf', 'application/x-pdf'));
$this->assertSame(['image/pdf', 'application/acrobat', 'application/nappdf'], (new Type('application/pdf'))->getAliases());
// Try removing a non-existing alias.
$this->assertFalse($this->map->removeTypeAlias('application/pdf', 'foo/bar'));
$this->assertSame(['image/pdf', 'application/acrobat', 'application/nappdf'], (new Type('application/pdf'))->getAliases());
// Remove an existing type.
$this->assertTrue($this->map->removeType('text/plain'));
$this->assertSame([], (new Type('text/plain'))->getExtensions(false));
$this->assertSame(null, (new Type('text/plain'))->getDefaultExtension(false));
$this->assertSame(['application/octet-stream'], (new Extension('DEf'))->getTypes(false));
$this->assertSame('application/octet-stream', (new Extension('DeF'))->getDefaultType(false));
// Remove an existing type with aliases.
$this->assertTrue($this->map->hasAlias('text/x-markdown'));
$this->assertTrue($this->map->removeType('text/markdown'));
$this->assertFalse($this->map->hasAlias('text/x-markdown'));
$this->assertSame([], (new Type('text/markdown'))->getExtensions(false));
$this->assertSame(null, (new Type('text/markdown'))->getDefaultExtension(false));
$this->assertSame([], (new Type('text/x-markdown'))->getExtensions(false));
$this->assertSame(null, (new Type('text/x-markdown'))->getDefaultExtension(false));
$this->assertSame(['application/octet-stream'], (new Extension('MD'))->getTypes(false));
$this->assertSame('application/octet-stream', (new Extension('md'))->getDefaultType(false));
// Try removing a non-existing type.
$this->assertFalse($this->map->removeType('axx/axx'));
}
public function testHasType()
{
$this->assertTrue($this->map->hasType('text/plain'));
$this->assertFalse($this->map->hasType('foo/bar'));
}
public function testHasAlias()
{
$this->assertTrue($this->map->hasAlias('application/acrobat'));
$this->assertFalse($this->map->hasAlias('foo/bar'));
}
public function testHasExtension()
{
$this->assertTrue($this->map->hasExtension('jpg'));
$this->assertFalse($this->map->hasExtension('jpgjpg'));
}
public function testSetExtensionDefaultType()
{
$this->assertSame(['text/vnd.dvb.subtitle', 'image/vnd.dvb.subtitle', 'text/x-microdvd', 'text/x-mpsub', 'text/x-subviewer'], (new Extension('sub'))->getTypes());
$this->map->setExtensionDefaultType('SUB', 'image/vnd.dvb.subtitle');
$this->assertSame(['image/vnd.dvb.subtitle', 'text/vnd.dvb.subtitle', 'text/x-microdvd', 'text/x-mpsub', 'text/x-subviewer'], (new Extension('SUB'))->getTypes());
}
public function testReAddAliasToType()
{
$this->assertSame(['image/psd', 'image/x-psd', 'image/photoshop', 'image/x-photoshop', 'application/photoshop', 'application/x-photoshop',], (new Type('image/vnd.adobe.photoshop'))->getAliases());
$this->map->addTypeAlias('image/vnd.adobe.photoshop', 'application/x-photoshop');
$this->assertSame(['image/psd', 'image/x-psd', 'image/photoshop', 'image/x-photoshop', 'application/photoshop', 'application/x-photoshop',], (new Type('image/vnd.adobe.photoshop'))->getAliases());
}
public function testAddAliasToMultipleTypes()
{
$this->assertSame([], (new Type('text/plain'))->getAliases());
$this->bcSetExpectedException(MappingException::class, "Cannot set 'application/x-photoshop' as alias for 'text/plain', it is an alias of 'image/vnd.adobe.photoshop' already");
$this->map->addTypeAlias('text/plain', 'application/x-photoshop');
$this->assertSame([], (new Type('text/plain'))->getAliases());
}
public function testAddAliasToMissingType()
{
$this->bcSetExpectedException(MappingException::class, "Cannot set 'baz/qoo' as alias for 'bar/foo', 'bar/foo' not defined");
$this->map->addTypeAlias('bar/foo', 'baz/qoo');
}
public function testAddAliasIsATypeAlready()
{
$this->bcSetExpectedException(MappingException::class, "Cannot set 'text/plain' as alias for 'text/richtext', 'text/plain' is already defined as a type");
$this->map->addTypeAlias('text/richtext', 'text/plain');
}
public function testAddDescriptionToAlias()
{
$this->bcSetExpectedException(MappingException::class, "Cannot add description for 'application/acrobat', 'application/acrobat' is an alias");
$this->map->addTypeDescription('application/acrobat', 'description of alias');
}
public function testSetExtensionDefaultTypeNoExtension()
{
$this->expectException(MappingException::class);
$this->map->setExtensionDefaultType('zxzx', 'image/vnd.dvb.subtitle');
}
public function testSetExtensionDefaultTypeNoType()
{
$this->expectException(MappingException::class);
$this->map->setExtensionDefaultType('sub', 'image/bingo');
}
/**
* Check that a type alias can be set as extension default.
*/
public function testSetExtensionDefaultTypeToAlias()
{
$this->assertSame(['application/pdf'], (new Extension('pdf'))->getTypes());
$this->map->setExtensionDefaultType('pdf', 'application/x-pdf');
$this->assertSame(['application/x-pdf', 'application/pdf'], (new Extension('pdf'))->getTypes());
$this->assertSame('application/x-pdf', (new Extension('pdf'))->getDefaultType());
$this->map->setExtensionDefaultType('pdf', 'image/pdf');
$this->assertSame(['image/pdf', 'application/x-pdf', 'application/pdf'], (new Extension('pdf'))->getTypes());
$this->assertSame('image/pdf', (new Extension('pdf'))->getDefaultType());
// Remove the alias, should be removed from extension types.
$this->assertTrue($this->map->removeTypeAlias('application/pdf', 'application/x-pdf'));
$this->assertSame(['image/pdf', 'application/pdf'], (new Extension('pdf'))->getTypes());
$this->assertSame('image/pdf', (new Extension('pdf'))->getDefaultType());
// Add a fake MIME type to 'psd', an alias to that, then remove
// 'image/vnd.adobe.photoshop'.
$this->assertSame(['image/vnd.adobe.photoshop'], (new Extension('psd'))->getTypes());
$this->assertSame('image/vnd.adobe.photoshop', (new Extension('psd'))->getDefaultType());
$this->map->setExtensionDefaultType('psd', 'image/psd');
$this->assertSame(['image/psd', 'image/vnd.adobe.photoshop'], (new Extension('psd'))->getTypes());
$this->assertSame('image/psd', (new Extension('psd'))->getDefaultType());
$this->map->addTypeExtensionMapping('bingo/bongo', 'psd');
$this->assertSame(['image/psd', 'image/vnd.adobe.photoshop', 'bingo/bongo'], (new Extension('psd'))->getTypes());
$this->map->addTypeAlias('bingo/bongo', 'bar/foo');
$this->assertSame(['image/psd', 'image/vnd.adobe.photoshop', 'bingo/bongo'], (new Extension('psd'))->getTypes());
$this->map->setExtensionDefaultType('psd', 'bar/foo');
$this->assertSame(['bar/foo', 'image/psd', 'image/vnd.adobe.photoshop', 'bingo/bongo'], (new Extension('psd'))->getTypes());
$this->assertTrue($this->map->removeType('image/vnd.adobe.photoshop'));
$this->assertSame(['bar/foo', 'bingo/bongo'], (new Extension('psd'))->getTypes());
}
/**
* Check removing an aliased type mapping.
*/
public function testRemoveAliasedTypeMapping()
{
$this->map->addTypeExtensionMapping('bingo/bongo', 'psd');
$this->assertSame(['image/vnd.adobe.photoshop', 'bingo/bongo'], (new Extension('psd'))->getTypes());
$this->map->addTypeAlias('bingo/bongo', 'bar/foo');
$this->assertSame(['image/vnd.adobe.photoshop', 'bingo/bongo'], (new Extension('psd'))->getTypes());
$this->map->setExtensionDefaultType('psd', 'bar/foo');
$this->assertSame(['bar/foo', 'image/vnd.adobe.photoshop', 'bingo/bongo'], (new Extension('psd'))->getTypes());
$this->map->removeTypeExtensionMapping('bar/foo', 'psd');
$this->assertSame(['image/vnd.adobe.photoshop', 'bingo/bongo'], (new Extension('psd'))->getTypes());
}
/**
* Check that a removing a type mapping also remove its aliases.
*/
public function testRemoveUnaliasedTypeMapping()
{
// Add a fake MIME type to 'psd', an alias to that, then remove
// 'image/vnd.adobe.photoshop'.
$this->map->addTypeExtensionMapping('bingo/bongo', 'psd');
$this->assertSame(['image/vnd.adobe.photoshop', 'bingo/bongo'], (new Extension('psd'))->getTypes());
$this->map->addTypeAlias('bingo/bongo', 'bar/foo');
$this->assertSame(['image/vnd.adobe.photoshop', 'bingo/bongo'], (new Extension('psd'))->getTypes());
$this->map->setExtensionDefaultType('psd', 'bar/foo');
$this->assertSame(['bar/foo', 'image/vnd.adobe.photoshop', 'bingo/bongo'], (new Extension('psd'))->getTypes());
$this->map->removeTypeExtensionMapping('bingo/bongo', 'psd');
$this->assertSame(['image/vnd.adobe.photoshop'], (new Extension('psd'))->getTypes());
}
public function testSetExtensionDefaultTypeToInvalidAlias()
{
$this->bcSetExpectedException(MappingException::class, "Cannot set 'image/psd' as default type for extension 'pdf', its unaliased type 'image/vnd.adobe.photoshop' is not associated to 'pdf'");
$this->map->setExtensionDefaultType('pdf', 'image/psd');
}
public function testSetTypeDefaultExtension()
{
$this->assertSame(['jpeg', 'jpg', 'jpe'], (new Type('image/jpeg'))->getExtensions());
$this->map->setTypeDefaultExtension('image/jpeg', 'jpg');
$this->assertSame(['jpg', 'jpeg', 'jpe'], (new Type('image/JPEG'))->getExtensions());
}
public function testSetTypeDefaultExtensionNoExtension()
{
$this->expectException(MappingException::class);
$this->map->setTypeDefaultExtension('image/jpeg', 'zxzx');
}
public function testSetTypeDefaultExtensionNoType()
{
$this->expectException(MappingException::class);
$this->map->setTypeDefaultExtension('image/bingo', 'jpg');
}
public function testAddExtensionToAlias()
{
$this->bcSetExpectedException(MappingException::class, "Cannot map 'pdf' to 'application/acrobat', 'application/acrobat' is an alias");
$this->map->addTypeExtensionMapping('application/acrobat', 'pdf');
}
/**
* Data provider for testAddMalformedTypeExtensionMapping.
*/
public function malformedTypeProvider()
{
return [
'empty string' => [''],
'n' => ['n'],
'no media' => ['/n'],
'no sub type' => ['n/'],
'no comment closing bracket a' => ['image (open ()/*'],
'no comment closing bracket b' => ['image / * (open (())'],
];
}
/**
* @dataProvider malformedTypeProvider
*/
public function testAddMalformedTypeExtensionMapping($type)
{
$this->expectException(MalformedTypeException::class);
$this->map->addTypeExtensionMapping($type, 'xxx');
}
}

View File

@@ -0,0 +1,144 @@
<?php
namespace FileEye\MimeMap\Test;
use Symfony\Component\Filesystem\Filesystem;
use FileEye\MimeMap\MapHandler;
use FileEye\MimeMap\MapUpdater;
/**
* @coversDefaultClass \FileEye\MimeMap\MapUpdater
* @backupStaticAttributes enabled
*/
class MapUpdaterTest extends MimeMapTestBase
{
protected $newMap;
protected $updater;
protected $fileSystem;
public function fcSetUp()
{
$this->updater = new MapUpdater();
$this->updater->selectBaseMap(MapUpdater::DEFAULT_BASE_MAP_CLASS);
$this->newMap = $this->updater->getMap();
$this->assertInstanceOf(MapUpdater::DEFAULT_BASE_MAP_CLASS, $this->newMap);
$this->fileSystem = new Filesystem();
}
public function fcTearDown()
{
$this->assertInstanceOf(MapUpdater::DEFAULT_BASE_MAP_CLASS, $this->newMap);
$this->newMap->reset();
}
public function testLoadMapFromApacheFile()
{
$this->updater->loadMapFromApacheFile(dirname(__FILE__) . '/../fixtures/min.mime-types.txt');
$expected = [
't' => [
'image/jpeg' => ['e' => ['jpeg', 'jpg', 'jpe']],
'text/plain' => ['e' => ['txt']],
],
'e' => [
'jpe' => ['t' => ['image/jpeg']],
'jpeg' => ['t' => ['image/jpeg']],
'jpg' => ['t' => ['image/jpeg']],
'txt' => ['t' => ['text/plain']],
],
];
$this->assertSame($expected, $this->newMap->getMapArray());
$this->assertSame(['image/jpeg', 'text/plain'], $this->newMap->listTypes());
$this->assertSame(['jpe', 'jpeg', 'jpg', 'txt'], $this->newMap->listExtensions());
$this->assertSame([], $this->newMap->listAliases());
}
public function testLoadMapFromApacheFileZeroLines()
{
$this->updater->loadMapFromApacheFile(dirname(__FILE__) . '/../fixtures/zero.mime-types.txt');
$this->assertSame([], $this->newMap->getMapArray());
}
public function testApplyOverridesFailure()
{
$this->updater->loadMapFromFreedesktopFile(dirname(__FILE__) . '/../fixtures/min.freedesktop.xml');
$errors = $this->updater->applyOverrides([['addTypeExtensionMapping', ['application/x-pdf', 'pdf']]]);
$this->assertSame(["Cannot map 'pdf' to 'application/x-pdf', 'application/x-pdf' is an alias"], $errors);
}
public function testLoadMapFromFreedesktopFile()
{
$this->updater->applyOverrides([['addTypeExtensionMapping', ['application/x-pdf', 'pdf']]]);
$errors = $this->updater->loadMapFromFreedesktopFile(dirname(__FILE__) . '/../fixtures/min.freedesktop.xml');
$this->assertSame(["Cannot set 'application/x-pdf' as alias for 'application/pdf', 'application/x-pdf' is already defined as a type"], $errors);
$expected = [
't' => [
'application/pdf' => [
'a' => ['image/pdf', 'application/acrobat', 'application/nappdf'],
'desc' => ['PDF document', 'PDF: Portable Document Format'],
'e' => ['pdf']
],
'application/x-atari-2600-rom' => [
'desc' => ['Atari 2600'],
'e' => ['a26']
],
'application/x-pdf' => [
'e' => ['pdf']
],
'text/plain' => [
'desc' => ['plain text document'],
'e' => ['txt', 'asc']
],
],
'e' => [
'a26' => ['t' => ['application/x-atari-2600-rom']],
'asc' => ['t' => ['text/plain']],
'pdf' => ['t' => ['application/x-pdf', 'application/pdf']],
'txt' => ['t' => ['text/plain']],
],
'a' => [
'application/acrobat' => ['t' => ['application/pdf']],
'application/nappdf' => ['t' => ['application/pdf']],
'image/pdf' => ['t' => ['application/pdf']],
],
];
$this->assertSame($expected, $this->newMap->getMapArray());
$this->assertSame(['application/pdf', 'application/x-atari-2600-rom', 'application/x-pdf', 'text/plain'], $this->newMap->listTypes());
$this->assertSame(['a26', 'asc', 'pdf', 'txt'], $this->newMap->listExtensions());
$this->assertSame(['application/acrobat', 'application/nappdf', 'image/pdf'], $this->newMap->listAliases());
}
public function testLoadMapFromFreedesktopFileZeroLines()
{
$this->updater->loadMapFromFreedesktopFile(dirname(__FILE__) . '/../fixtures/zero.freedesktop.xml');
$this->assertSame([], $this->newMap->getMapArray());
}
public function testEmptyMapNotWriteable()
{
$this->expectException('LogicException');
$this->assertNull($this->newMap->getFileName());
}
public function testWriteMapToPhpClassFile()
{
$this->fileSystem->copy(__DIR__ . '/../../src/Map/MiniMap.php.test', __DIR__ . '/../../src/Map/MiniMap.php');
MapHandler::setDefaultMapClass('\FileEye\MimeMap\Map\MiniMap');
$map_a = MapHandler::map();
$this->assertStringContainsString('src/Map/MiniMap.php', $map_a->getFileName());
$content = file_get_contents($map_a->getFileName());
$this->assertStringNotContainsString('text/plain', $content);
$this->updater->loadMapFromApacheFile(dirname(__FILE__) . '/../fixtures/min.mime-types.txt');
$this->updater->applyOverrides([['addTypeExtensionMapping', ['bing/bong', 'binbon']]]);
$this->updater->writeMapToPhpClassFile($map_a->getFileName());
$content = file_get_contents($map_a->getFileName());
$this->assertStringContainsString('text/plain', $content);
$this->assertStringContainsString('bing/bong', $content);
$this->assertStringContainsString('binbon', $content);
$this->fileSystem->remove(__DIR__ . '/../../src/Map/MiniMap.php');
}
public function testGetDefaultMapBuildFile()
{
$this->assertStringContainsString('default_map_build.yml', MapUpdater::getDefaultMapBuildFile());
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace FileEye\MimeMap\Test;
use FileEye\MimeMap\Extension;
use PHPUnit\Framework\TestCase;
use PHPUnit\Runner\Version;
// In order to manage different method signatures between PHPUnit versions, we
// dynamically load a compatibility trait dependent on the PHPUnit runner
// version.
// phpcs:disable
if (!trait_exists('FileEye\MimeMap\Test\PhpunitCompatibilityTrait', false)) {
if (class_exists('PHPUnit\Runner\Version') && version_compare(Version::id(), '8.0.0', '>=')) {
class_alias('FileEye\MimeMap\Test\Phpunit8CompatibilityTrait', 'FileEye\MimeMap\Test\PhpunitCompatibilityTrait');
} elseif (class_exists('PHPUnit\Runner\Version') && version_compare(Version::id(), '7.0.0', '>=')) {
class_alias('FileEye\MimeMap\Test\Phpunit7CompatibilityTrait', 'FileEye\MimeMap\Test\PhpunitCompatibilityTrait');
} else {
class_alias('FileEye\MimeMap\Test\Phpunit5CompatibilityTrait', 'FileEye\MimeMap\Test\PhpunitCompatibilityTrait');
}
}
// phpcs:enable
class MimeMapTestBase extends TestCase
{
use PhpunitCompatibilityTrait;
}

View File

@@ -0,0 +1,81 @@
<?php
namespace FileEye\MimeMap\Test;
// PHPUnit compatibility trait for PHPUnit 5.
trait Phpunit5CompatibilityTrait
{
/**
* Checks if the trait is used in a class that has a method.
*
* @param string $method
* Method to check.
*
* @return bool
* TRUE if the method is supported, FALSE if not.
*/
private function supports($method)
{
// Get the parent class of the currently running test class.
$parent = get_parent_class($this);
// Ensure that the method_exists() check on the createMock method is carried
// out on the first parent of $this that does not have access to this
// trait's methods. This is because the trait also has a method called
// createMock(). Most often the check will be made on
// \PHPUnit\Framework\TestCase.
while (method_exists($parent, 'supports')) {
$parent = get_parent_class($parent);
}
return method_exists($parent, $method);
}
public function setUp()
{
parent::setUp();
if (method_exists($this, 'fcSetUp')) {
$this->fcSetUp();
}
}
public function tearDown()
{
if (method_exists($this, 'fcTearDown')) {
$this->fcTearDown();
}
parent::tearDown();
}
public function bcSetExpectedException($exceptionName, $exceptionMessage = '', $exceptionCode = null)
{
if ($this->supports('expectException')) {
parent::expectException($exceptionName);
if ($exceptionMessage) {
parent::expectExceptionMessage($exceptionMessage);
}
if ($exceptionCode !== null) {
parent::expectExceptionCode($exceptionCode);
}
} else {
parent::setExpectedException($exceptionName, $exceptionMessage, $exceptionCode);
}
}
public function expectException($exception)
{
if ($this->supports('expectException')) {
parent::expectException($exception);
} else {
parent::setExpectedException($exception);
}
}
public function assertStringContainsString($needle, $haystack, $message = '')
{
parent::assertContains($needle, $haystack, $message);
}
public function assertStringNotContainsString($needle, $haystack, $message = '')
{
parent::assertNotContains($needle, $haystack, $message);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace FileEye\MimeMap\Test;
// PHPUnit compatibility trait for PHPUnit 7.
trait Phpunit7CompatibilityTrait
{
public function setUp()
{
parent::setUp();
if (method_exists($this, 'fcSetUp')) {
$this->fcSetUp();
}
}
public function tearDown()
{
if (method_exists($this, 'fcTearDown')) {
$this->fcTearDown();
}
parent::tearDown();
}
public function bcSetExpectedException($exceptionName, $exceptionMessage = '', $exceptionCode = null)
{
parent::expectException($exceptionName);
if ($exceptionMessage) {
parent::expectExceptionMessage($exceptionMessage);
}
if ($exceptionCode !== null) {
parent::expectExceptionCode($exceptionCode);
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace FileEye\MimeMap\Test;
// PHPUnit compatibility trait for PHPUnit 8.
trait Phpunit8CompatibilityTrait
{
public function setUp(): void
{
parent::setUp();
if (method_exists($this, 'fcSetUp')) {
$this->fcSetUp();
}
}
public function tearDown(): void
{
if (method_exists($this, 'fcTearDown')) {
$this->fcTearDown();
}
parent::tearDown();
}
public function bcSetExpectedException($exceptionName, $exceptionMessage = '', $exceptionCode = null)
{
parent::expectException($exceptionName);
if ($exceptionMessage) {
parent::expectExceptionMessage($exceptionMessage);
}
if ($exceptionCode !== null) {
parent::expectExceptionCode($exceptionCode);
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FileEye\MimeMap\Test;
use FileEye\MimeMap\Type;
use FileEye\MimeMap\TypeParameter;
class TypeParameterTest extends MimeMapTestBase
{
public function testHasComment()
{
$mt = new Type('image/png; a="parameter" (with a comment)');
$this->assertSame('a', $mt->getParameter('a')->getName());
$this->assertSame('parameter', $mt->getParameter('a')->getValue());
$this->assertTrue($mt->getParameter('a')->hasComment());
$this->assertSame('with a comment', $mt->getParameter('a')->getComment());
$mt = new Type('image/png;param=foo(with a comment)');
$this->assertSame('param', $mt->getParameter('param')->getName());
$this->assertSame('foo', $mt->getParameter('param')->getValue());
$this->assertTrue($mt->getParameter('param')->hasComment());
$this->assertSame('with a comment', $mt->getParameter('param')->getComment());
}
public function testHasCommentNegative()
{
$mt = new Type('image/png; a="parameter"');
$this->assertSame('a', $mt->getParameter('a')->getName());
$this->assertSame('parameter', $mt->getParameter('a')->getValue());
$this->assertFalse($mt->getParameter('a')->hasComment());
$mt = new Type('image/png;foo=bar');
$this->assertSame('foo', $mt->getParameter('foo')->getName());
$this->assertSame('bar', $mt->getParameter('foo')->getValue());
$this->assertFalse($mt->getParameter('foo')->hasComment());
}
}

View File

@@ -0,0 +1,657 @@
<?php
namespace FileEye\MimeMap\Test;
use FileEye\MimeMap\MalformedTypeException;
use FileEye\MimeMap\MappingException;
use FileEye\MimeMap\Type;
use FileEye\MimeMap\TypeParameter;
class TypeTest extends MimeMapTestBase
{
/**
* Data provider for testParse.
*/
public function parseProvider()
{
return [
'application/ogg;description=Hello there!;asd=fgh' => [
'application/ogg;description=Hello there!;asd=fgh',
[
'application/ogg',
'application/ogg; description="Hello there!"; asd="fgh"',
'application/ogg; description="Hello there!"; asd="fgh"',
],
['application', null],
['ogg', null],
true,
[
'description' => ['Hello there!', null],
'asd' => ['fgh', null],
],
],
'text/plain' => [
'text/plain',
[
'text/plain',
'text/plain',
'text/plain',
],
['text', null],
['plain', null],
false,
[],
],
'text/plain;a=b' => [
'text/plain;a=b',
[
'text/plain',
'text/plain; a="b"',
'text/plain; a="b"',
],
['text', null],
['plain', null],
true,
[
'a' => ['b', null],
],
],
'application/ogg' => [
'application/ogg',
[
'application/ogg',
'application/ogg',
'application/ogg',
],
['application', null],
['ogg', null],
false,
[],
],
'*/*' => [
'*/*',
[
'*/*',
'*/*',
'*/*',
],
['*', null],
['*', null],
false,
[],
],
'n/n' => [
'n/n',
[
'n/n',
'n/n',
'n/n',
],
['n', null],
['n', null],
false,
[],
],
'(UTF-8 Plain Text) text / plain ; charset = utf-8' => [
'(UTF-8 Plain Text) text / plain ; charset = utf-8',
[
'text/plain',
'text/plain; charset="utf-8"',
'text (UTF-8 Plain Text)/plain; charset="utf-8"',
],
['text', 'UTF-8 Plain Text'],
['plain', null],
true,
[
'charset' => ['utf-8', null],
],
],
'text (Text) / plain ; charset = utf-8' => [
'text (Text) / plain ; charset = utf-8',
[
'text/plain',
'text/plain; charset="utf-8"',
'text (Text)/plain; charset="utf-8"',
],
['text', 'Text'],
['plain', null],
true,
[
'charset' => ['utf-8', null],
],
],
'text / (Plain) plain ; charset = utf-8' => [
'text / (Plain) plain ; charset = utf-8',
[
'text/plain',
'text/plain; charset="utf-8"',
'text/plain (Plain); charset="utf-8"',
],
['text', null],
['plain', 'Plain'],
true,
[
'charset' => ['utf-8', null],
],
],
'text / plain (Plain Text) ; charset = utf-8' => [
'text / plain (Plain Text) ; charset = utf-8',
[
'text/plain',
'text/plain; charset="utf-8"',
'text/plain (Plain Text); charset="utf-8"',
],
['text', null],
['plain', 'Plain Text'],
true,
[
'charset' => ['utf-8', null],
],
],
'text / plain ; (Charset=utf-8) charset = utf-8' => [
'text / plain ; (Charset=utf-8) charset = utf-8',
[
'text/plain',
'text/plain; charset="utf-8"',
'text/plain; charset="utf-8" (Charset=utf-8)',
],
['text', null],
['plain', null],
true,
[
'charset' => ['utf-8', 'Charset=utf-8'],
],
],
'text / plain ; charset (Charset) = utf-8' => [
'text / plain ; charset (Charset) = utf-8',
[
'text/plain',
'text/plain; charset="utf-8"',
'text/plain; charset="utf-8" (Charset)',
],
['text', null],
['plain', null],
true,
[
'charset' => ['utf-8', 'Charset'],
],
],
'text / plain ; charset = (UTF8) utf-8' => [
'text / plain ; charset = (UTF8) utf-8',
[
'text/plain',
'text/plain; charset="utf-8"',
'text/plain; charset="utf-8" (UTF8)',
],
['text', null],
['plain', null],
true,
[
'charset' => ['utf-8', 'UTF8'],
],
],
'text / plain ; charset = utf-8 (UTF-8 Plain Text)' => [
'text / plain ; charset = utf-8 (UTF-8 Plain Text)',
[
'text/plain',
'text/plain; charset="utf-8"',
'text/plain; charset="utf-8" (UTF-8 Plain Text)',
],
['text', null],
['plain', null],
true,
[
'charset' => ['utf-8', 'UTF-8 Plain Text'],
],
],
'application/x-foobar;description="bbgh(kdur"' => [
'application/x-foobar;description="bbgh(kdur"',
[
'application/x-foobar',
'application/x-foobar; description="bbgh(kdur"',
'application/x-foobar; description="bbgh(kdur"',
],
['application', null],
['x-foobar', null],
true,
[
'description' => ['bbgh(kdur', null],
],
],
'application/x-foobar;description="a \"quoted string\""' => [
'application/x-foobar;description="a \"quoted string\""',
[
'application/x-foobar',
'application/x-foobar; description="a \"quoted string\""',
'application/x-foobar; description="a \"quoted string\""',
],
['application', null],
['x-foobar', null],
true,
[
'description' => ['a "quoted string"', null],
],
],
'text/xml;description=test' => [
'text/xml;description=test',
[
'text/xml',
'text/xml; description="test"',
'text/xml; description="test"',
],
['text', null],
['xml', null],
true,
[
'description' => ['test', null],
],
],
'text/xml;one=test;two=three' => [
'text/xml;one=test;two=three',
[
'text/xml',
'text/xml; one="test"; two="three"',
'text/xml; one="test"; two="three"',
],
['text', null],
['xml', null],
true,
[
'one' => ['test', null],
'two' => ['three', null],
],
],
'text/xml;one="test";two="three"' => [
'text/xml;one="test";two="three"',
[
'text/xml',
'text/xml; one="test"; two="three"',
'text/xml; one="test"; two="three"',
],
['text', null],
['xml', null],
true,
[
'one' => ['test', null],
'two' => ['three', null],
],
],
'text/xml; this="is"; a="parameter" (with a comment)' => [
'text/xml; this="is"; a="parameter" (with a comment)',
[
'text/xml',
'text/xml; this="is"; a="parameter"',
'text/xml; this="is"; a="parameter" (with a comment)',
],
['text', null],
['xml', null],
true,
[
'this' => ['is', null],
'a' => ['parameter', 'with a comment'],
],
],
// Various edge cases.
'null' => [
null,
[
null,
null,
null,
],
[null, null],
[null, null],
false,
[],
],
'text/plain; charset="utf-8" (UTF/8)' => [
'text/plain; charset="utf-8" (UTF/8)',
[
'text/plain',
'text/plain; charset="utf-8"',
'text/plain; charset="utf-8" (UTF/8)',
],
['text', null],
['plain', null],
true,
[
'charset' => ['utf-8', 'UTF/8'],
],
],
'appf/xml; a=b; b="parameter" (with; a comment) ;c=d; e=f (;) ; g=h ' => [
'appf/xml; a=b; b="parameter" (with; a comment) ;c=d; e=f (;) ; g=h ',
[
'appf/xml',
'appf/xml; a="b"; b="parameter"; c="d"; e="f"; g="h"',
'appf/xml; a="b"; b="parameter" (with; a comment); c="d"; e="f" (;); g="h"',
],
['appf', null],
['xml', null],
true,
[
'a' => ['b', null],
'b' => ['parameter', 'with; a comment'],
'c' => ['d', null],
'e' => ['f', ';'],
'g' => ['h', null],
],
],
'text/(abc)def(ghi)' => [
'text/(abc)def(ghi)',
[
'text/def',
'text/def',
'text/def (abc ghi)',
],
['text', null],
['def', 'abc ghi'],
false,
[],
],
'text/(abc)def' => [
'text/(abc)def',
[
'text/def',
'text/def',
'text/def (abc)',
],
['text', null],
['def', 'abc'],
false,
[],
],
'text/def(ghi)' => [
'text/def(ghi)',
[
'text/def',
'text/def',
'text/def (ghi)',
],
['text', null],
['def', 'ghi'],
false,
[],
],
'text/plain;a=(\)abc)def(\()' => [
'text/plain;a=(\)abc)def(\()',
[
'text/plain',
'text/plain; a="def"',
'text/plain; a="def" (\)abc \()',
],
['text', null],
['plain', null],
true,
[
'a' => ['def', '\)abc \('],
],
],
'text/plain;a=\\foo(abc)' => [
'text/plain;a=\\foo(abc)',
[
'text/plain',
'text/plain; a="foo"',
'text/plain; a="foo" (abc)',
],
['text', null],
['plain', null],
true,
[
'a' => ['foo', 'abc'],
],
],
'text/plain;a=(a"bc\)def")def' => [
'text/plain;a=(a"bc\)def")def',
[
'text/plain',
'text/plain; a="def"',
'text/plain; a="def" (a"bc\)def")',
],
['text', null],
['plain', null],
true,
[
'a' => ['def', 'a"bc\)def"'],
],
],
'text/plain;a="(abc)def"' => [
'text/plain;a="(abc)def"',
[
'text/plain',
'text/plain; a="(abc)def"',
'text/plain; a="(abc)def"',
],
['text', null],
['plain', null],
true,
[
'a' => ['(abc)def', null],
],
],
];
}
/**
* @dataProvider parseProvider
*/
public function testParse($type, array $expectedToString, array $expectedMedia, array $expectedSubType, $expectedHasParameters, array $expectedParameters)
{
$mt = new Type($type);
$this->assertSame($expectedMedia[0], $mt->getMedia());
$this->assertSame($expectedMedia[1], $mt->getMediaComment());
$this->assertSame($expectedSubType[0], $mt->getSubType());
$this->assertSame($expectedSubType[1], $mt->getSubTypeComment());
$this->assertSame($expectedHasParameters, $mt->hasParameters());
$this->assertSame(count($expectedParameters), count($mt->getParameters()));
foreach ($expectedParameters as $name => $param) {
$this->assertTrue(isset($mt->getParameters()[$name]));
$this->assertInstanceOf(TypeParameter::class, $mt->getParameter($name));
$this->assertSame($name, $mt->getParameter($name)->getName());
$this->assertSame($param[0], $mt->getParameter($name)->getValue());
$this->assertSame($param[1], $mt->getParameter($name)->getComment());
}
$this->assertSame($expectedToString[0], $mt->toString(Type::SHORT_TEXT));
$this->assertSame($expectedToString[1], $mt->toString(Type::FULL_TEXT));
$this->assertSame($expectedToString[2], $mt->toString(Type::FULL_TEXT_WITH_COMMENTS));
}
/**
* Data provider for testParseMalformed.
*/
public function parseMalformedProvider()
{
return [
'empty string' => [''],
'n' => ['n'],
'no media' => ['/n'],
'no sub type' => ['n/'],
'no comment closing bracket a' => ['image (open ()/*'],
'no comment closing bracket b' => ['image / * (open (())'],
];
}
/**
* @dataProvider parseMalformedProvider
*/
public function testParseMalformed($type)
{
$this->expectException(MalformedTypeException::class);
new Type($type);
}
public function testParseAgain()
{
$mt = new Type('application/ogg;description=Hello there!;asd=fgh');
$this->assertSame(2, count($mt->getParameters()));
$mt = new Type('text/plain;hello=there!');
$this->assertSame(1, count($mt->getParameters()));
}
public function testGetDescription()
{
$this->assertNull((new Type('*/*'))->getDescription());
$this->assertNull((new Type('image/*'))->getDescription());
$this->assertNull((new Type('application/java*'))->getDescription());
$this->assertNull((new Type('application/x-t3vm-image'))->getDescription());
$this->assertSame('HTML document', (new Type('text/html'))->getDescription());
$this->assertSame('HTML document, HTML: HyperText Markup Language', (new Type('text/html'))->getDescription(true));
$this->assertSame('GPX geographic data', (new Type('application/gpx+xml'))->getDescription());
$this->assertSame('GPX geographic data, GPX: GPS Exchange Format', (new Type('application/gpx+xml'))->getDescription(true));
$this->assertSame('GPX geographic data', (new Type('application/gpx'))->getDescription());
$this->assertSame('GPX geographic data, GPX: GPS Exchange Format', (new Type('application/gpx'))->getDescription(true));
$this->assertSame('GPX geographic data', (new Type('application/x-gpx'))->getDescription());
$this->assertSame('GPX geographic data, GPX: GPS Exchange Format', (new Type('application/x-gpx'))->getDescription(true));
}
public function testSetComment()
{
$type = new Type('text/x-test');
$type->setMediaComment('media comment');
$this->assertSame('text (media comment)/x-test', $type->toString(Type::FULL_TEXT_WITH_COMMENTS));
$type->setSubTypeComment('subtype comment');
$this->assertSame('text (media comment)/x-test (subtype comment)', $type->toString(Type::FULL_TEXT_WITH_COMMENTS));
$type->setMediaComment(null);
$this->assertSame('text/x-test (subtype comment)', $type->toString(Type::FULL_TEXT_WITH_COMMENTS));
$type->setSubTypeComment(null);
$this->assertSame('text/x-test', $type->toString(Type::FULL_TEXT_WITH_COMMENTS));
}
public function testIsExperimental()
{
$this->assertTrue((new Type('text/x-test'))->isExperimental());
$this->assertTrue((new Type('image/X-test'))->isExperimental());
$this->assertFalse((new Type('text/plain'))->isExperimental());
}
public function testIsVendor()
{
$this->assertTrue((new Type('application/vnd.openoffice'))->isVendor());
$this->assertFalse((new Type('application/vendor.openoffice'))->isVendor());
$this->assertFalse((new Type('vnd/fsck'))->isVendor());
}
public function testIsWildcard()
{
$this->assertTrue((new Type('*/*'))->isWildcard());
$this->assertTrue((new Type('image/*'))->isWildcard());
$this->assertFalse((new Type('text/plain'))->isWildcard());
$this->assertTrue((new Type('application/java*'))->isWildcard());
$this->assertTrue((new Type('application/java-*'))->isWildcard());
}
public function testIsAlias()
{
$this->assertFalse((new Type('*/*'))->isAlias());
$this->assertFalse((new Type('image/*'))->isAlias());
$this->assertFalse((new Type('text/plain'))->isAlias());
$this->assertFalse((new Type('application/java*'))->isAlias());
$this->assertTrue((new Type('text/x-markdown'))->isAlias());
}
public function testWildcardMatch()
{
$this->assertTrue((new Type('image/png'))->wildcardMatch('*/*'));
$this->assertTrue((new Type('image/png'))->wildcardMatch('image/*'));
$this->assertFalse((new Type('text/plain'))->wildcardMatch('image/*'));
$this->assertFalse((new Type('image/png'))->wildcardMatch('image/foo'));
$this->assertTrue((new Type('application/javascript'))->wildcardMatch('application/java*'));
$this->assertTrue((new Type('application/java-serialized-object'))->wildcardMatch('application/java-*'));
$this->assertFalse((new Type('application/javascript'))->wildcardMatch('application/java-*'));
}
public function testAddParameter()
{
$mt = new Type('image/png; foo=bar');
$mt->addParameter('baz', 'val', 'this is a comment');
$res = $mt->toString(Type::FULL_TEXT_WITH_COMMENTS);
$this->assertStringContainsString('foo=', $res);
$this->assertStringContainsString('bar', $res);
$this->assertStringContainsString('baz=', $res);
$this->assertStringContainsString('val', $res);
$this->assertStringContainsString('(this is a comment)', $res);
$this->assertSame('image/png; foo="bar"; baz="val" (this is a comment)', $res);
}
public function testRemoveParameter()
{
$mt = new Type('image/png; foo=bar;baz=val(this is a comment)');
$mt->removeParameter('foo');
$res = $mt->toString(Type::FULL_TEXT_WITH_COMMENTS);
$this->assertStringNotContainsString('foo=', $res);
$this->assertStringNotContainsString('bar', $res);
$this->assertStringContainsString('baz=', $res);
$this->assertSame('image/png; baz="val" (this is a comment)', $res);
}
public function testGetAliases()
{
$this->assertSame(['image/x-wmf', 'image/x-win-metafile', 'application/x-wmf', 'application/wmf', 'application/x-msmetafile'], (new Type('image/wmf'))->getAliases());
$this->assertSame([], (new Type('foo/bar'))->getAliases(false));
$this->assertSame([], (new Type('image/x-wmf'))->getAliases(false));
}
public function testGetAliasesOnAliasStrict()
{
$this->bcSetExpectedException(MappingException::class, "Cannot get aliases for 'image/x-wmf', it is an alias itself");
$this->assertSame([], (new Type('image/x-wmf'))->getAliases());
}
public function testGetAliasesOnMissingTypeStrict()
{
$this->bcSetExpectedException(MappingException::class, "No MIME type found for foo/bar in map");
$this->assertSame([], (new Type('foo/bar'))->getAliases());
}
public function testGetExtensions()
{
$this->assertEquals(['atom'], (new Type('application/atom+xml'))->getExtensions());
$this->assertEquals(['ser', 'js', 'jsm', 'mjs'], (new Type('application/java*'))->getExtensions());
$this->assertEquals(['ser'], (new Type('application/java-*'))->getExtensions());
$this->assertEquals([], (new Type('application/a000'))->getExtensions(false));
$this->assertEquals([], (new Type('application/a000-*'))->getExtensions(false));
$this->assertSame(['smi', 'smil', 'sml', 'kino'], (new Type('application/smil+xml'))->getExtensions());
$this->assertSame(['smi', 'smil', 'sml', 'kino'], (new Type('application/smil'))->getExtensions());
}
public function testGetExtensionsFail()
{
$this->expectException(MappingException::class);
$this->assertEquals([], (new Type('application/a000'))->getExtensions());
}
public function testGetDefaultExtension()
{
$this->assertEquals('atom', (new Type('application/atom+xml'))->getDefaultExtension());
$this->assertEquals('csv', (new Type('text/csv'))->getDefaultExtension());
$this->assertSame('smi', (new Type('application/smil+xml'))->getDefaultExtension());
$this->assertSame('smi', (new Type('application/smil'))->getDefaultExtension());
}
/**
* Data provider for testGetDefaultExtensionFail.
*/
public function getDefaultExtensionFailProvider()
{
return [
['*/*'],
['n/n'],
['image/*'],
['application/java*'],
];
}
/**
* @dataProvider getDefaultExtensionFailProvider
*/
public function testGetDefaultExtensionFail($type)
{
$this->expectException(MappingException::class);
$this->assertNull((new Type($type))->getDefaultExtension());
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace FileEye\MimeMap\Test;
use FileEye\MimeMap\Version;
class VersionTest extends MimeMapTestBase
{
public function testGet()
{
$this->assertNotNull(Version::get());
}
}