PHP Classes

File: src/Plugin/Request/CallableClass/ComponentOptions.php

Recommend this page to a friend!
  Packages of Thierry Feuzeu   Jaxon   src/Plugin/Request/CallableClass/ComponentOptions.php   Download  
File: src/Plugin/Request/CallableClass/ComponentOptions.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: 1 year ago
Size: 9,883 bytes
 

Contents

Class file image Download
<?php

/**
 * ComponentOptions.php
 *
 * Options of a callable object.
 *
 * @package jaxon-core
 * @author Thierry Feuzeu <thierry.feuzeu@gmail.com>
 * @copyright 2024 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\Plugin\Request\CallableClass;

use function array_merge;
use function array_unique;
use function explode;
use function in_array;
use function is_array;
use function is_string;
use function json_encode;
use function substr;
use function str_replace;
use function trim;

class ComponentOptions
{
    /**
     * Check if the js code for this object must be generated
     *
     * @var bool
     */
    private $bExcluded = false;

    /**
     * The character to use as separator in javascript class names
     *
     * @var string
     */
    private $sSeparator = '.';

    /**
     * A list of methods of the user registered callable object the library must not export to javascript
     *
     * @var array
     */
    private $aProtectedMethods = [];

    /**
     * A list of methods to call before processing the request
     *
     * @var array
     */
    private $aBeforeMethods = [];

    /**
     * A list of methods to call after processing the request
     *
     * @var array
     */
    private $aAfterMethods = [];

    /**
     * The javascript class options
     *
     * @var array
     */
    private $aJsOptions = [];

    /**
     * The DI options
     *
     * @var array
     */
    private $aDiOptions = [];

    /**
     * The constructor
     *
     * @param array $aOptions
     * @param bool $bExcluded
     * @param array $aProtectedMethods
     * @param array $aProperties
     */
    public function __construct(array $aOptions, bool $bExcluded = false,
        array $aProtectedMethods = [], array $aProperties = [])
    {
        $this->bExcluded = $bExcluded || (bool)($aOptions['excluded'] ?? false);
        if($this->bExcluded)
        {
            return;
        }

        $sSeparator = $aOptions['separator'];
        if($sSeparator === '_' || $sSeparator === '.')
        {
            $this->sSeparator = $sSeparator;
        }
        $this->addProtectedMethods($aOptions['protected']);
        $this->addProtectedMethods($aProtectedMethods);

        foreach($aOptions['functions'] as $sNames => $aFunctionOptions)
        {
            // Names are in a comma-separated list.
            $aFunctionNames = explode(',', $sNames);
            foreach($aFunctionNames as $sFunctionName)
            {
                $this->addFunctionOptions($sFunctionName, $aFunctionOptions);
            }
        }
        foreach($aProperties as $sFunctionName => $aFunctionOptions)
        {
            $this->addFunctionOptions($sFunctionName, $aFunctionOptions);
        }
    }

    /**
     * @param array|string $xMethods
     *
     * @return void
     */
    private function addProtectedMethods(array|string $xMethods): void
    {
        if(!is_array($xMethods))
        {
            $this->aProtectedMethods[trim((string)$xMethods)] = true;
            return;
        }

        foreach($xMethods as $sMethod)
        {
            $this->aProtectedMethods[trim((string)$sMethod)] = true;
        }
    }

    /**
     * @param string $sMethodName
     *
     * @return bool
     */
    public function isProtectedMethod(string $sMethodName): bool
    {
        return isset($this->aProtectedMethods['*']) ||
            isset($this->aProtectedMethods[$sMethodName]);
    }

    /**
     * Check if the js code for this object must be generated
     *
     * @return bool
     */
    public function excluded(): bool
    {
        return $this->bExcluded;
    }

    /**
     * @return string
     */
    public function separator(): string
    {
        return $this->sSeparator;
    }

    /**
     * @return array
     */
    public function beforeMethods(): array
    {
        return $this->aBeforeMethods;
    }

    /**
     * @return array
     */
    public function afterMethods(): array
    {
        return $this->aAfterMethods;
    }

    /**
     * @return array
     */
    public function diOptions(): array
    {
        return $this->aDiOptions;
    }

    /**
     * @return array
     */
    public function jsOptions(): array
    {
        return $this->aJsOptions;
    }

    /**
     * Set hook methods
     *
     * @param array $aHookMethods    The array of hook methods
     * @param string|array $xValue    The value of the configuration option
     *
     * @return void
     */
    private function setHookMethods(array &$aHookMethods, $xValue): void
    {
        foreach($xValue as $sCalledMethod => $xMethodToCall)
        {
            if(!isset($aHookMethods[$sCalledMethod]))
            {
                $aHookMethods[$sCalledMethod] = [];
            }
            if(is_array($xMethodToCall))
            {
                $aHookMethods[$sCalledMethod] = array_merge($aHookMethods[$sCalledMethod], $xMethodToCall);
            }
            elseif(is_string($xMethodToCall))
            {
                $aHookMethods[$sCalledMethod][] = $xMethodToCall;
            }
        }
    }

    /**
     * @param array $aDiOptions
     */
    private function addDiOption(array $aDiOptions): void
    {
        $this->aDiOptions = array_merge($this->aDiOptions, $aDiOptions);
    }

    /**
     * Set configuration options / call options for each method
     *
     * @param string $sName    The name of the configuration option
     * @param string|array $xValue    The value of the configuration option
     *
     * @return void
     */
    private function addOption(string $sName, $xValue): void
    {
        switch($sName)
        {
        // Set the methods to call before processing the request
        case '__before':
            $this->setHookMethods($this->aBeforeMethods, $xValue);
            break;
        // Set the methods to call after processing the request
        case '__after':
            $this->setHookMethods($this->aAfterMethods, $xValue);
            break;
        // Set the attributes to inject in the callable object
        case '__di':
            $this->addDiOption($xValue);
            break;
        default:
            break;
        }
    }

    /**
     * @param string $sFunctionName
     * @param string $sOptionName
     * @param mixed $xOptionValue
     *
     * @return void
     */
    private function _addJsArrayOption(string $sFunctionName, string $sOptionName, $xOptionValue): void
    {
        if(is_string($xOptionValue))
        {
            $xOptionValue = [$xOptionValue];
        }
        if(!is_array($xOptionValue))
        {
            return; // Do not save.
        }

        $aOptions = $this->aJsOptions[$sFunctionName][$sOptionName] ?? [];
        $this->aJsOptions[$sFunctionName][$sOptionName] = array_merge($aOptions, $xOptionValue);
    }

    /**
     * @param string $sFunctionName
     * @param string $sOptionName
     * @param mixed $xOptionValue
     *
     * @return void
     */
    private function _setJsOption(string $sFunctionName, string $sOptionName, $xOptionValue): void
    {
        $this->aJsOptions[$sFunctionName][$sOptionName] = $xOptionValue;
    }

    /**
     * @param string $sFunctionName
     * @param string $sOptionName
     * @param mixed $xOptionValue
     *
     * @return void
     */
    private function addJsOption(string $sFunctionName, string $sOptionName, $xOptionValue): void
    {
        switch($sOptionName)
        {
        case 'excluded':
            if((bool)$xOptionValue)
            {
                $this->addProtectedMethods($sFunctionName);
            }
            break;
        // For databags and callbacks, all the value are merged in a single array.
        case 'bags':
        case 'callback':
            $this->_addJsArrayOption($sFunctionName, $sOptionName, $xOptionValue);
            return;
        // For all the other options, only the last value is kept.
        default:
            $this->_setJsOption($sFunctionName, $sOptionName, $xOptionValue);
        }
    }

    /**
     * @param string $sFunctionName
     * @param array $aFunctionOptions
     *
     * @return void
     */
    private function addFunctionOptions(string $sFunctionName, array $aFunctionOptions): void
    {
        foreach($aFunctionOptions as $sOptionName => $xOptionValue)
        {
            substr($sOptionName, 0, 2) === '__' ?
                // Options for PHP classes. They start with "__".
                $this->addOption($sOptionName, [$sFunctionName => $xOptionValue]) :
                // Options for javascript code.
                $this->addJsOption($sFunctionName, $sOptionName, $xOptionValue);
        }
    }

    /**
     * @param string $sMethodName
     *
     * @return array
     */
    public function getMethodOptions(string $sMethodName): array
    {
        // First take the common options.
        $aOptions = array_merge($this->aJsOptions['*'] ?? []); // Clone the array
        // Then add the method options.
        $aMethodOptions = $this->aJsOptions[$sMethodName] ?? [];
        foreach($aMethodOptions as $sOptionName => $xOptionValue)
        {
            // For databags and callbacks, merge the values in a single array.
            // For all the other options, keep the last value.
            $aOptions[$sOptionName] = !in_array($sOptionName, ['bags', 'callback']) ?
                $xOptionValue :
                array_unique(array_merge($aOptions[$sOptionName] ?? [], $xOptionValue));
        }
        // Since callbacks are js object names, they need a special formatting.
        if(isset($aOptions['callback']))
        {
            $aOptions['callback'] = str_replace('"', '', json_encode($aOptions['callback']));
        }
        return $aOptions;
    }
}