Here's a full-fledged factory object which demonstrates auto-loading, namespaces support, callables from non-static instances (with variable paths), handling of loading errors and custom exceptions.
abstract class AbstractFactory implements \ArrayAccess
{
    protected $manifest;
    function __construct($manifest)
    {
        $this->manifest = $manifest;
    }
    abstract function produce($name);
    public function offsetExists($offset)
    {
        return isset($this->manifest[$offset]);
    }
    public function offsetGet($offset)
    {
        return $this->produce($offset);
    }
    //implement stubs for other ArrayAccess funcs
}
abstract class SimpleFactory extends AbstractFactory {
    protected $description;
    protected $path;
    protected $namespace;
    function __construct($manifest, $path, $namespace = "jj\\") {
        parent::__construct($manifest);
        $this->path = $path;
        $this->namespace = $namespace;
        if (! spl_autoload_register(array($this, 'autoload'), false)) //throws exceptions on its own, but we want a custom one
            throw new \RuntimeException(get_class($this)." failed to register autoload.");
    }
    function __destruct()
    {
        spl_autoload_unregister(array($this, 'autoload'));
    }
    public function autoload($class_name) {
        $file = str_replace($this->namespace, '', $class_name);
        $filename = $this->path.$file.'.php';
        if (file_exists($filename))
            try {
                require $filename; //TODO add global set_error_handler and try clause to catch parse errors
            } catch (Exception $e) {} //autoload exceptions are not passed by design, nothing to do
    }
    function produce($name) {
        if (isset($this->manifest[$name])) {
            $class = $this->namespace.$this->manifest[$name];
            if (class_exists($class, $autoload = true)) {
                return new $class();
            } else throw new \jj\SystemConfigurationException('Factory '.get_class($this)." was unable to produce a new class {$class}", 'SYSTEM_ERROR', $this);
//an example of a custom exception with a string code and data container
        } else throw new LogicException("Unknown {$this->description} {$name}.");
    }
    function __toString() //description function if custom exception class wants a string explanation for its container
    {
        return $this->description." factory ".get_class($this)."(path={$this->path}, namespace={$this->namespace}, map: ".json_encode($this->manifest).")";
    }
}
and finally an example:
namespace jj;
require_once('lib/AbstractFactory.php');
require_once('lib/CurrenciesProvider.php'); //base abstract class for all banking objects that are created
class CurrencyProviders extends SimpleFactory
{
    function __construct()
    {
        $manifest = array(
          'Germany' => 'GermanBankCurrencies',
          'Switzerland' => 'SwissBankCurrencies'
        );
        parent::__construct($manifest, __DIR__.'/CurrencyProviders/', //you have total control over relative or absolute paths here
       'banks\');
        $this->description = 'currency provider country name';
    }
}
now do
$currencies_cache = (new \jj\CurrencyProviders())['Germany'];
or
$currencies_cache = (new \jj\CurrencyProviders())['Ukraine'];
LogicException("Unknown currency provider country name Ukraine")
If there is no SwissCurrencies.php file in /CurrencyProviders/,
\jj\SystemConfigurationException('Factory jj\CurrencyProviders was unable to produce a new class banks\SwissCurrencies. Debug data: currency provider country name factory jj\CurrencyProviders(path=/var/www/hosted/site/.../CurrencyProviders/, namespace=banks\, map: {"Germany": "GermanBankCurrencies", "Switzerland":"SwissBankCurrencies"}')
With enough effort this factory can be extended to catch parse errors (How to catch error of require() or include() in PHP?) and pass arguments to constructors.