PHP Classes

File: src/Request/Plugin/CallableClass.php

Recommend this page to a friend!
  Classes of Thierry Feuzeu   Jaxon   src/Request/Plugin/CallableClass.php   Download  
File: src/Request/Plugin/CallableClass.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: 4 years ago
Size: 9,751 bytes
 

Contents

Class file image Download
<?php

/**
 * CallableClass.php - Jaxon callable class plugin
 *
 * This class registers user defined callable classes, generates client side javascript code,
 * and calls their methods on user request
 *
 * @package jaxon-core
 * @author Jared White
 * @author J. Max Wilson
 * @author Joseph Woolley
 * @author Steffen Konerow
 * @author Thierry Feuzeu <thierry.feuzeu@gmail.com>
 * @copyright Copyright (c) 2005-2007 by Jared White & J. Max Wilson
 * @copyright Copyright (c) 2008-2010 by Joseph Woolley, Steffen Konerow, Jared White & J. Max Wilson
 * @copyright 2016 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\Plugin;

use
Jaxon\Jaxon;
use
Jaxon\CallableClass as UserCallableClass;
use
Jaxon\Plugin\Request as RequestPlugin;
use
Jaxon\Request\Support\CallableObject;
use
Jaxon\Request\Support\CallableRegistry;
use
Jaxon\Request\Support\CallableRepository;
use
Jaxon\Request\Target;

class
CallableClass extends RequestPlugin
{
    use \
Jaxon\Features\Config;
    use \
Jaxon\Features\Template;
    use \
Jaxon\Features\Validator;
    use \
Jaxon\Features\Translator;

   
/**
     * The callable registrar
     *
     * @var CallableRegistry
     */
   
protected $xRegistry;

   
/**
     * The callable repository
     *
     * @var CallableRepository
     */
   
protected $xRepository;

   
/**
     * The value of the class parameter of the incoming Jaxon request
     *
     * @var string
     */
   
protected $sRequestedClass = '';

   
/**
     * The value of the method parameter of the incoming Jaxon request
     *
     * @var string
     */
   
protected $sRequestedMethod = '';

   
/**
     * The class constructor
     *
     * @param CallableRegistry $xRegistry The callable class registry
     * @param CallableRepository $xRepository The callable object repository
     */
   
public function __construct(CallableRegistry $xRegistry, CallableRepository $xRepository)
    {
       
$this->xRegistry = $xRegistry;
       
$this->xRepository = $xRepository;

        if(!empty(
$_GET['jxncls']))
        {
           
$this->sRequestedClass = trim($_GET['jxncls']);
        }
        if(!empty(
$_GET['jxnmthd']))
        {
           
$this->sRequestedMethod = trim($_GET['jxnmthd']);
        }
        if(!empty(
$_POST['jxncls']))
        {
           
$this->sRequestedClass = trim($_POST['jxncls']);
        }
        if(!empty(
$_POST['jxnmthd']))
        {
           
$this->sRequestedMethod = trim($_POST['jxnmthd']);
        }
    }

   
/**
     * @inheritDoc
     */
   
public function getName()
    {
        return
Jaxon::CALLABLE_CLASS;
    }

   
/**
     * @inheritDoc
     */
   
public function getTarget()
    {
        if(!
$this->sRequestedClass || !$this->sRequestedMethod)
        {
            return
null;
        }
        return
Target::makeClass($this->sRequestedClass, $this->sRequestedMethod);
    }

   
/**
     * Register a callable class
     *
     * @param string $sType The type of request handler being registered
     * @param string $sClassName The name of the class being registered
     * @param array|string $aOptions The associated options
     *
     * @return boolean
     */
   
public function register($sType, $sClassName, $aOptions)
    {
       
$sType = trim($sType);
        if(
$sType != $this->getName())
        {
            return
false;
        }

        if(!
is_string($sClassName))
        {
            throw new \
Jaxon\Exception\Error($this->trans('errors.objects.invalid-declaration'));
        }
        if(
is_string($aOptions))
        {
           
$aOptions = ['include' => $aOptions];
        }
        if(!
is_array($aOptions))
        {
            throw new \
Jaxon\Exception\Error($this->trans('errors.objects.invalid-declaration'));
        }

       
$this->xRepository->addClass(trim($sClassName), $aOptions);

        return
true;
    }

   
/**
     * @inheritDoc
     */
   
public function getHash()
    {
       
$this->xRegistry->createCallableClasses();
       
$aNamespaces = $this->xRepository->getNamespaces();
       
$aClasses = $this->xRepository->getClasses();
       
$sHash = '';

        foreach(
$aNamespaces as $sNamespace => $aOptions)
        {
           
$sHash .= $sNamespace . $aOptions['separator'];
        }
        foreach(
$aClasses as $sClassName => $aOptions)
        {
           
$sHash .= $sClassName . $aOptions['timestamp'];
        }

        return
md5($sHash);
    }

   
/**
     * Generate client side javascript code for namespaces
     *
     * @return string
     */
   
private function getNamespacesScript()
    {
       
$sCode = '';
       
$sPrefix = $this->getOption('core.prefix.class');
       
$aJsClasses = [];
       
$aNamespaces = array_keys($this->xRepository->getNamespaces());
        foreach(
$aNamespaces as $sNamespace)
        {
           
$offset = 0;
           
$sJsNamespace = str_replace('\\', '.', $sNamespace);
           
$sJsNamespace .= '.Null'; // This is a sentinel. The last token is not processed in the while loop.
           
while(($dotPosition = strpos($sJsNamespace, '.', $offset)) !== false)
            {
               
$sJsClass = substr($sJsNamespace, 0, $dotPosition);
               
// Generate code for this object
               
if(!key_exists($sJsClass, $aJsClasses))
                {
                   
$sCode .= "$sPrefix$sJsClass = {};\n";
                   
$aJsClasses[$sJsClass] = $sJsClass;
                }
               
$offset = $dotPosition + 1;
            }
        }
        return
$sCode;
    }

   
/**
     * Generate client side javascript code for a callable class
     *
     * @param string $sClassName The class name
     * @param CallableObject $xCallableObject The corresponding callable object
     * @param array $aProtectedMethods The protected methods
     *
     * @return string
     */
   
private function getCallableScript($sClassName, CallableObject $xCallableObject, array $aProtectedMethods)
    {
       
$aCallableOptions = $this->xRepository->getCallableOptions();
       
$aConfig = $aCallableOptions[$sClassName];
       
$aCommonConfig = key_exists('*', $aConfig) ? $aConfig['*'] : [];

       
$_aProtectedMethods = is_subclass_of($sClassName, UserCallableClass::class) ? $aProtectedMethods : [];
       
$aMethods = [];
        foreach(
$xCallableObject->getMethods() as $sMethodName)
        {
           
// Don't export methods of the CallableClass class
           
if(in_array($sMethodName, $_aProtectedMethods))
            {
                continue;
            }
           
// Specific options for this method
           
$aMethodConfig = key_exists($sMethodName, $aConfig) ?
               
array_merge($aCommonConfig, $aConfig[$sMethodName]) : $aCommonConfig;
           
$aMethods[] = [
               
'name' => $sMethodName,
               
'config' => $aMethodConfig,
            ];
        }

       
$sPrefix = $this->getOption('core.prefix.class');
        return
$this->render('jaxon::support/object.js', [
           
'sPrefix' => $sPrefix,
           
'sClass' => $xCallableObject->getJsName(),
           
'aMethods' => $aMethods,
        ]);
    }

   
/**
     * Generate client side javascript code for the registered callable objects
     *
     * @return string
     */
   
public function getScript()
    {
       
$this->xRegistry->createCallableObjects();

       
// The methods of the \Jaxon\CallableClass class must not be exported
       
$xCallableClass = new \ReflectionClass(UserCallableClass::class);
       
$aProtectedMethods = [];
        foreach(
$xCallableClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $xMethod)
        {
           
$aProtectedMethods[] = $xMethod->getName();
        }

       
$sCode = $this->getNamespacesScript();

       
$aCallableObjects = $this->xRepository->getCallableObjects();
        foreach(
$aCallableObjects as $sClassName => $xCallableObject)
        {
           
$sCode .= $this->getCallableScript($sClassName, $xCallableObject, $aProtectedMethods);
        }

        return
$sCode;
    }

   
/**
     * @inheritDoc
     */
   
public function canProcessRequest()
    {
       
// Check the validity of the class name
       
if(($this->sRequestedClass !== null && !$this->validateClass($this->sRequestedClass)) ||
            (
$this->sRequestedMethod !== null && !$this->validateMethod($this->sRequestedMethod)))
        {
           
$this->sRequestedClass = null;
           
$this->sRequestedMethod = null;
        }
        return (
$this->sRequestedClass !== null && $this->sRequestedMethod !== null);
    }

   
/**
     * @inheritDoc
     */
   
public function processRequest()
    {
        if(!
$this->canProcessRequest())
        {
            return
false;
        }

       
// Find the requested method
       
$xCallableObject = $this->xRegistry->getCallableObject($this->sRequestedClass);
        if(!
$xCallableObject || !$xCallableObject->hasMethod($this->sRequestedMethod))
        {
           
// Unable to find the requested object or method
           
throw new \Jaxon\Exception\Error($this->trans('errors.objects.invalid',
                [
'class' => $this->sRequestedClass, 'method' => $this->sRequestedMethod]));
        }

       
// Call the requested method
       
$di = jaxon()->di();
       
$aArgs = $di->getRequestHandler()->processArguments();
       
$xResponse = $xCallableObject->call($this->sRequestedMethod, $aArgs);
        if((
$xResponse))
        {
           
$di->getResponseManager()->append($xResponse);
        }
        return
true;
    }
}