有些对象其实我们只需要一个,比如:线程池、缓存、对话框、处理偏好设置和注册表的对象、日志对象,充当打印机、显卡等设备的驱动程序的对象。多个实例反而会造成很多问题。

单件模式的目标:确保一个类只有一个实例,并提供一个全局访问点。

涉及Symfony组件:HttpFoundation

MimeTypeGuesser.php

一个单件MIME类型猜测器,默认所有的MIME类型猜测器都由已安装的框架提供(如果在当前 OS/PHP 安装中可得的话)。

你可以通过此单件实例的 register() 方法来注册自定义猜测器。自定义猜测器总是在默认的猜测器之前被调用。

 $guesser = MimeTypeGuesser::getInstance();
 $guesser->register(new MyCustomMimeTypeGuesser());
class MimeTypeGuesser implements MimeTypeGuesserInterface
{
    /**
     * 单件实例
     *
     * @var MimeTypeGuesser
     */
    private static $instance = null;

    /**
     * 所有已注册的 MimeTypeGuesserInterface 实例。
     *
     * @var array
     */
    protected $guessers = array();

    /**
     * 返回此单件实例。
     *
     * @return self
     */
    public static function getInstance()
    {
        if (null === self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * 重置此单件实例
     */
    public static function reset()
    {
        self::$instance = null;
    }

    /**
     * 登记所有本机提供的MIME类型猜测器
     */
    private function __construct()
    {
        if (FileBinaryMimeTypeGuesser::isSupported()) {
            $this->register(new FileBinaryMimeTypeGuesser());
        }

        if (FileinfoMimeTypeGuesser::isSupported()) {
            $this->register(new FileinfoMimeTypeGuesser());
        }
    }

    /**
     * 登记一个新的MIME类型猜测器。
     *
     * 当猜测时,这个猜测器优先于之前注册的。
     */
    public function register(MimeTypeGuesserInterface $guesser)
    {
        array_unshift($this->guessers, $guesser);
    }

    /**
     * 尝试猜测给定文件的MIME类型
     *
     * 该文件按照注册的相反顺序传递给每个注册的MIME类型猜测器(最后注册的先查询)。
     * 一旦猜测者返回一个不为 NULL 的值,该方法终止并返回值。
     * 
     * @param string $path 到文件的路径
     *
     * @return string MIME 类型或 NULL,如果什么也猜不到的话
     *
     * @throws \LogicException
     * @throws FileNotFoundException
     * @throws AccessDeniedException
     */
    public function guess($path)
    {
        if (!is_file($path)) {
            throw new FileNotFoundException($path);
        }

        if (!is_readable($path)) {
            throw new AccessDeniedException($path);
        }

        if (!$this->guessers) {
            $msg = 'Unable to guess the mime type as no guessers are available';
            if (!FileinfoMimeTypeGuesser::isSupported()) {
                $msg .= ' (Did you enable the php_fileinfo extension?)';
            }
            throw new \LogicException($msg);
        }

        foreach ($this->guessers as $guesser) {
            if (null !== $mimeType = $guesser->guess($path)) {
                return $mimeType;
            }
        }
    }
}