Scanner.php

<?php

namespace Lia\CompoTrait;

/**
 * Wire methods with prefixes to their relevant APIs.
 *
 * All methods in this trait are prefixed with `Scanner_` to prevent naming conflicts. Some convenience methods without the prefix are given, too. 
 * Generally, override the convenience methods, not the `Scanner_` ones
 *
 * @tag component, internals
 * @todo create 'getGlobalPrefixes()', 'getOwnPrefixes()', and 'getAllPrefixes()' (which combines the two prior).
 * @todo I hate the `Scanner_` prefix used by the Scanner trait... Idk. Idk. Maybe change it?
 * @todo add 'getPrefixRegex()' method to configure what pattern a prefix has to be
 * @todo add a 'parsePrefix()' function that removes the prefix_, & replaces _ with '.'. Simply override it to do custom parsing (or none at all)
 * @todo add support for Attributes. And/or add a sibling-naming convention... `prefixWhatever`, 
 *        - Sibling-namings: `scannedPrefixWhatever(){return $thePreparedStuffYouNeed}` or something. Or leave that up to the Apis. Ex: sitemapBlog(), routeBlog(), handleBlog()
 * @todo review method names for intuitiveness
 */
trait Scanner {


    /**
     * @deprecated I don't think this is used anywhere any more. 
     * @todo delete this property (and test)
     */
    protected $Scanner_registered = [];

    /**
     * Get array of global api prefixes
     * From getApiPrefixes(), you can call $this->Scanner_getApiPrefixes() to get the list from liaison.
     * If you overide this method:
     *     `$prefixes = array_replace_recursive($this->Scanner_getApiPrefixes(), $apiPrefixes);`
     */
    public function getApiPrefixes(){
        return $this->Scanner_getApiPrefixes();
    }
    /**
     * Get the api for the given prefix. Returns an array with index 0 == apiname & index 1 == handlerName
     * Get api for given prefix. 
     * @return array like [0=>api.name, 1=>handlerName]. 
     */
    public function getApiForPrefix($prefix){
        return $this->Scanner_getApiForPrefix($prefix);
    }
    
    /**
     * Get array of method names that have the prefix
     *
     * @param $prefix the prefix to filter for
     * @return array of method names
     */
    public function getMethodsWithPrefix($prefix){
        return $this->Scanner_getMethodsWithPrefix($prefix);
    }

    /**
     * Scan for all prefixed methods on this class & call the relevant API's prefix handler
     */
    public function autoHandlePrefixedMethods(){
        return $this->Scanner_autoHandlePrefixedMethods();
    }

    /**
     * Scan for the given prefix & map it to the given api (or the configured api);
     * 
     */
    public function autoHandlePrefix($prefix, $api=null){
        return $this->Scanner_autoHandlePrefix($prefix, $api);
    }


    /**
     * Get array of prefixes from `Liaison` like `[ 'prefix'=> ['api.key', 'handlerName'], 'prefix2'=>...]`
     *
     */
    public function Scanner_getApiPrefixes(){
        $lia = $this->liaison ?? $this->lia ?? null;
        if ($lia==null)return [];
        $prefixes = $lia->getApiPrefixes();
        return $prefixes;
    }

    /**
     * Returns an array of  `[$methodNameWithoutPrefix => $methodNameWithPrefix]`
     *
     * @return array like `[ '_MethodName' => 'prefix_MethodName', 'Another'=>'prefixAnother']`
     */
    protected function Scanner_getMethodsWithPrefix($prefix){
        $methods = get_class_methods($this);

        $found = [];
        foreach ($methods as $method){
            if (!preg_match('/^'.$prefix.'(([A-Z])|(\_[a-zA-Z]))/',$method,$matches))continue;
            $start = $matches[0];
            $name = substr($method,strlen($start)-1);
            if (substr($start, -2, 1)=='_')$name = '_'.$name;
            $found[$name] = $method;
        }
        return $found;
    }

    /**
     * Get api for `$prefix`
     *
     * @return array like `['api.key', 'handlerName']`
     */
    protected function Scanner_getApiForPrefix($prefix){
        $prefixes = $this->getApiPrefixes();
        if (isset($prefixes[$prefix]))return $prefixes[$prefix];

        throw new \Lia\Exception\Base("Prefix '{$prefix}' is not defined globally. In this component, override getApiForPrefix() or getApiPrefixes() to if you don't want to use global prefixes.");
    }



    /**
     * Scan for all prefixed methods on this class & call the relevant API's prefix handler
     *
     */
    public function Scanner_autoHandlePrefixedMethods(){
        $prefixes = $this->getApiPrefixes();
        // print_r($prefixes);
        foreach ($prefixes as $prefix => $api){
            $this->autoHandlePrefix($prefix, $api);
        }
    }

    /**
     * Scan for the given prefix & map it to the given api (or the configured api);
     * 
     */
    public function Scanner_autoHandlePrefix($prefix, $api=null){
        $liaison = $liaison ?? $this->liaison ?? $this->lia ?? null;
        if (isset($this->Scanner_prefixesHandled[$prefix]))return;
        if ($api==null)$api = $this->getApiForPrefix($prefix);

        $this->Scanner_prefixesHandled[$prefix] = $prefix;
        $methods = $this->getMethodsWithPrefix($prefix);
        foreach ($methods as $cleanName => $actualMethodName){
            // Intention is to point to the 'prefix' handler, thus $api[1], generally should be 'prefix', but this can be modified.
            $liaison->api($api, $cleanName, [$this, $actualMethodName]);
        }
    }

}