File: src/Request/Support/CallableRegistry.php

Recommend this page to a friend!
  Classes of Thierry Feuzeu  >  Jaxon  >  src/Request/Support/CallableRegistry.php  >  Download  
File: src/Request/Support/CallableRegistry.php
Role: Class source
Content type: text/plain
Description: Class source
Class: Jaxon
Call PHP classes from JavaScript using AJAX
Author: By
Last change:
Date: 7 months ago
Size: 10,379 bytes
 

Contents

Class file image Download
<?php

/**
 * CallableRegistry.php - Jaxon callable object registrar
 *
 * This class is the entry point for class, directory and namespace registration.
 *
 * @package jaxon-core
 * @author Thierry Feuzeu <thierry.feuzeu@gmail.com>
 * @copyright 2019 Thierry Feuzeu <thierry.feuzeu@gmail.com>
 * @license https://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
 * @link https://github.com/jaxon-php/jaxon-core
 */

namespace Jaxon\Request\Support;

use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;

class CallableRegistry
{
    /**
     * The callable repository
     *
     * @var CallableRepository
     */
    public $xRepository;

    /**
     * The registered directories
     *
     * These are directories registered without a namespace.
     *
     * @var array
     */
    protected $aDirectories = [];

    /**
     * Indicate if the registered directories are already parsed
     *
     * @var boolean
     */
    protected $bParsedDirectories = false;

    /**
     * The registered namespaces
     *
     * These are the namespaces specified when registering directories.
     *
     * @var array
     */
    protected $aNamespaces = [];

    /**
     * Indicate if the registered namespaces are already parsed
     *
     * @var boolean
     */
    protected $bParsedNamespaces = false;

    /**
     * If the underscore is used as separator in js class names.
     *
     * @var boolean
     */
    protected $bUsingUnderscore = false;

    /**
     * The Composer autoloader
     *
     * @var Autoloader
     */
    private $xAutoloader = null;

    /**
     * The class constructor
     *
     * @param CallableRepository        $xRepository
     */
    public function __construct(CallableRepository $xRepository)
    {
        $this->xRepository = $xRepository;

        // Set the composer autoloader
        $sAutoloadFile = __DIR__ . '/../../../../../autoload.php';
        if(file_exists($sAutoloadFile))
        {
            $this->xAutoloader = require($sAutoloadFile);
        }
    }

    /**
     *
     * @param string        $sDirectory     The directory being registered
     * @param array         $aOptions       The associated options
     *
     * @return void
     */
    public function addDirectory($sDirectory, array $aOptions)
    {
        // Set the autoload option default value
        if(!key_exists('autoload', $aOptions))
        {
            $aOptions['autoload'] = true;
        }

        $this->aDirectories[$sDirectory] = $aOptions;
    }

    /**
     *
     * @param string        $sNamespace     The namespace of the directory being registered
     * @param array         $aOptions       The associated options
     *
     * @return void
     */
    public function addNamespace($sNamespace, array $aOptions)
    {
        // Separator default value
        if(!key_exists('separator', $aOptions))
        {
            $aOptions['separator'] = '.';
        }
        $aOptions['separator'] = trim($aOptions['separator']);
        if(!in_array($aOptions['separator'], ['.', '_']))
        {
            $aOptions['separator'] = '.';
        }
        if($aOptions['separator'] == '_')
        {
            $this->bUsingUnderscore = true;
        }
        // Set the autoload option default value
        if(!key_exists('autoload', $aOptions))
        {
            $aOptions['autoload'] = true;
        }
        // Register the dir with PSR4 autoloading
        if(($aOptions['autoload']) && $this->xAutoloader != null)
        {
            $this->xAutoloader->setPsr4($sNamespace . '\\', $aOptions['directory']);
        }

        $this->aNamespaces[$sNamespace] = $aOptions;
    }

    /**
     * Read classes from registered directories (without namespaces)
     *
     * @return void
     */
    protected function parseDirectories()
    {
        // Browse directories without namespaces and read all the files.
        // This is to be done only once.
        if($this->bParsedDirectories)
        {
            return;
        }
        $this->bParsedDirectories = true;

        foreach($this->aDirectories as $sDirectory => $aOptions)
        {
            $itFile = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($sDirectory));
            // Iterate on dir content
            foreach($itFile as $xFile)
            {
                // skip everything except PHP files
                if(!$xFile->isFile() || $xFile->getExtension() != 'php')
                {
                    continue;
                }

                $sClassName = $xFile->getBasename('.php');
                $aClassOptions = ['timestamp' => $xFile->getMTime()];
                // No more classmap autoloading. The file will be included when needed.
                if(($aOptions['autoload']))
                {
                    $aClassOptions['include'] = $xFile->getPathname();
                }
                $this->xRepository->addClass($sClassName, $aClassOptions, $aOptions);
            }
        }
    }

    /**
     * Read classes from registered directories (with namespaces)
     *
     * @return void
     */
    protected function parseNamespaces()
    {
        // Browse directories with namespaces and read all the files.
        // This is to be done only once.
        if($this->bParsedNamespaces)
        {
            return;
        }
        $this->bParsedNamespaces = true;

        $sDS = DIRECTORY_SEPARATOR;
        foreach($this->aNamespaces as $sNamespace => $aOptions)
        {
            $this->xRepository->addNamespace($sNamespace, ['separator' => $aOptions['separator']]);

            // Iterate on dir content
            $sDirectory = $aOptions['directory'];
            $itFile = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($sDirectory));
            foreach($itFile as $xFile)
            {
                // skip everything except PHP files
                if(!$xFile->isFile() || $xFile->getExtension() != 'php')
                {
                    continue;
                }

                // Find the class path (the same as the class namespace)
                $sClassPath = $sNamespace;
                $sRelativePath = substr($xFile->getPath(), strlen($sDirectory));
                $sRelativePath = trim(str_replace($sDS, '\\', $sRelativePath), '\\');
                if($sRelativePath != '')
                {
                    $sClassPath .= '\\' . $sRelativePath;
                }

                $this->xRepository->addNamespace($sClassPath, ['separator' => $aOptions['separator']]);

                $sClassName = $sClassPath . '\\' . $xFile->getBasename('.php');
                $aClassOptions = ['namespace' => $sNamespace, 'timestamp' => $xFile->getMTime()];
                $this->xRepository->addClass($sClassName, $aClassOptions, $aOptions);
            }
        }
    }

    /**
     * Find options for a class which is registered with namespace
     *
     * @param string        $sClassName            The class name
     *
     * @return array|null
     */
    protected function getClassOptionsFromNamespaces($sClassName)
    {
        // Find the corresponding namespace
        $sNamespace = null;
        foreach(array_keys($this->aNamespaces) as $_sNamespace)
        {
            if(substr($sClassName, 0, strlen($_sNamespace) + 1) == $_sNamespace . '\\')
            {
                $sNamespace = $_sNamespace;
                break;
            }
        }
        if($sNamespace === null)
        {
            return null; // Class not registered
        }

        // Get the class options
        $aOptions = $this->aNamespaces[$sNamespace];
        $aClassOptions = ['namespace' => $sNamespace];
        return $this->xRepository->makeClassOptions($sClassName, $aClassOptions, $aOptions);
    }

    /**
     * Find the options associated with a registered class name
     *
     * @param string        $sClassName            The class name
     *
     * @return array|null
     */
    protected function getClassOptions($sClassName)
    {
        // Find options for a class registered with namespace.
        $aOptions = $this->getClassOptionsFromNamespaces($sClassName);
        if($aOptions !== null)
        {
            return $aOptions;
        }

        // Without a namespace, we need to parse all classes to be able to find one.
        $this->parseDirectories();

        // Find options for a class registered without namespace.
        return $this->xRepository->getClassOptions($sClassName);
    }

    /**
     * Find a callable object by class name
     *
     * @param string        $sClassName            The class name of the callable object
     *
     * @return CallableObject
     */
    public function getCallableObject($sClassName)
    {
        // Replace all separators ('.' and '_') with antislashes, and remove the antislashes
        // at the beginning and the end of the class name.
        $sClassName = (string)$sClassName;
        $sClassName = trim(str_replace('.', '\\', $sClassName), '\\');
        if($this->bUsingUnderscore)
        {
            $sClassName = trim(str_replace('_', '\\', $sClassName), '\\');
        }

        // Check if the callable object was already created.
        if(($xCallableObject = $this->xRepository->getCallableObject($sClassName)) != null)
        {
            return $xCallableObject;
        }

        $aOptions = $this->getClassOptions($sClassName);
        if($aOptions === null)
        {
            return null;
        }

        return $this->xRepository->createCallableObject($sClassName, $aOptions);
    }

    /**
     * Create callable objects for all registered classes
     *
     * @return void
     */
    public function createCallableClasses()
    {
        $this->parseDirectories();
        $this->parseNamespaces();
    }

    /**
     * Create callable objects for all registered classes
     *
     * @return void
     */
    public function createCallableObjects()
    {
        $this->createCallableClasses();

        foreach($this->xRepository->getClasses() as $sClassName => $aClassOptions)
        {
            // Make sure we create each callable object only once.
            if(!$this->xRepository->getCallableObject($sClassName))
            {
                $this->xRepository->createCallableObject($sClassName, $aClassOptions);
            }
        }
    }
}

For more information send a message to info at phpclasses dot org.