3
\$\begingroup\$

There isn't a very reliable way (in my opinion) to get the mime type of a specific extension, regardless of the file actually being of that type or not.

This type of thing usually is used when sending a different Content-type when you intend to send, for example, an image.

class MIMETypes {

    /**
     * @var string Contains the default type when none is found
     * @access protected
     */
    static private $default='application/octet-stream';

    /**
     * @var array Contains all the mime types and extensions
     * @access protected
     */
    static private $mimes=array(
        'text'=>array(
            'plain'=>array(
                'php','php5','php4','php3','inc','sphp','phps','phtml',
                'htm','html',
                'txt'
            )
        ),
        'audio'=>array(
            'aac'=>array('aac'),
            'midi'=>array('mid','midi'),
            'mp4'=>array('mp4','m4a'),
            'mpeg'=>array('m2a','mp1','mp2','mp3','mp4','mpg','mpeg','mpa','mpga'),
            'ogg'=>array('oga','ogg'),
            'wav'=>array('wav','wave','pcm'),
            'webm'=>array('webm'),
            'x-mpequrl'=>array('m3u')
        ),
        'video'=>array(
            'mp4'=>array('mp4','m4v','m1v','m2v'),
            'ogg'=>array('ogv'),
            'webm'=>array('webm')
        )
    );

    /**
     * @var array Contains the last accessed contents and comes pre-initialized with some extensions and mime types that didn't matched
     * @access protected
     */     
    static private $cache=array(
        'ext'=>array(
            'txt'=>array('plain/text')
        ),
        'mime'=>array(
            '*/*'=>array()
        )
    );

    /**
     * Creates a list of matching mime types for that file extension
     * @param string $ext The extension to look for
     * @param string|null $hint Hint to reduce the search load for only that mime "group"<br/>
     *    *WARNING*: Setting this parameter will ignore the cache!
     * @return array An array with the multiple mime types
     */
    static function fromExt( $ext, $hint=null )
    {
        //this will have all matched mime types
        $exts = array();

        //clean up the $ext var, to hold nothing but an extension
        $ext = preg_replace( '@^(?:.*\.)*([^.\\]+)$@', '$1', $ext );

        if( func_num_args() > 1 )
        {
            $hint = strtolower( trim($hint) );

            foreach( self::$mimes[$hint] as $mime=>$types)
            {
                if( in_array( $ext, $types) )
                {
                    $exts[] = $hint . '/' . $mime;
                }
            }
        }
        else if( self::$cache['ext'][$ext] )
        {
            return self::$cache['ext'][$ext];
        }
        else
        {
            foreach( self::$mimes as $mime=>$mimes)
            {
                foreach( $mimes as $type=>$types)
                {
                    if( in_array( $ext, $list) )
                    {
                        $exts[] = $mime . '/' . $type;
                    }
                }
            }

            if(!$exts)
            {
                $exts=array( self::$default );
            }
        }

        return self::$cache['ext'][$ext] = $exts;
    }

    /**
     * Creates a list of matching extensions for that mime type
     * @param string $mime The extension to look for
     * @return array An array with the multiple extensions
     */
    static function fromMIME( $mime )
    {
        //'clean' blindly-exploded mime type 
        $mime_c=explode('/', $mime, 2);

        if( self::$cache['mime'][$mime] )
        {
            return array(); //self::$cache['mime'][$mime];
        }
        else if( self::$mimes[$mime_c[0]] )
        {
            return self::$mimes[$mime_c[0]][$mime_c[1]];
        }
        else
        {
            return self::$cache['mime'][$mime] = array();
        }
    }
}

The functioning of the class is complete, but I'm aware that there are lots of missing mime types like the image/* or application/*.

What can I do to improve my code in quality and make a better class and (maybe) make it production-ready?

What should I improve in terms of performance?

\$\endgroup\$
  • \$\begingroup\$ What if you have two different mime types that share the same file extension? This is a possibility. Personally, I would determine the mime type based on the data contain in the file, and cache the findings. Then when the file is requested, get the mime type from the cache. \$\endgroup\$ – Professor of programming Jan 7 '15 at 16:12
  • \$\begingroup\$ @Bonner If you already have a 'hint' on what mime type the file will have (if you are dealing with audio files, for example), you can set the 2nd parameter on the method fromExt(). E.g.: MIMETypes::fromExt('mp4','audio') and it should return an array with only 1 mime type. If you don't set, it will return an array with all the mime types matching that extension. An example would be the extension mp4 (MIMETypes::fromExt('mp4')) which will return an array containing the mime types audio/mp4 and video/mp4. \$\endgroup\$ – Ismael Miguel Jan 7 '15 at 18:05
2
\$\begingroup\$

This is mostly a style review, but I'll be back to work on it further later.


Your usage of whitespace leaves a bit to be desired.

  1. You should have whitespace in between your key => value, rather than none.
  2. You should have spaces between commas
  3. You should have spaces in between your binary operators static private $default='application/octet-stream'
  4. You shouldn't have whitespace before and after your brackets

You should consider moving the regex statement in the following line to an external variable.

$ext = preg_replace( '@^(?:.*\.)*([^.\\]+)$@', '$1', $ext );
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.