brainworxx/kreXX

View on GitHub
src/Analyse/Callback/Iterate/ThroughGetter.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php

/**
 * kreXX: Krumo eXXtended
 *
 * kreXX is a debugging tool, which displays structured information
 * about any PHP object. It is a nice replacement for print_r() or var_dump()
 * which are used by a lot of PHP developers.
 *
 * kreXX is a fork of Krumo, which was originally written by:
 * Kaloyan K. Tsvetkov <kaloyan@kaloyan.info>
 *
 * @author
 *   brainworXX GmbH <info@brainworxx.de>
 *
 * @license
 *   http://opensource.org/licenses/LGPL-2.1
 *
 *   GNU Lesser General Public License Version 2.1
 *
 *   kreXX Copyright (C) 2014-2024 Brainworxx GmbH
 *
 *   This library is free software; you can redistribute it and/or modify it
 *   under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation; either version 2.1 of the License, or (at
 *   your option) any later version.
 *   This library is distributed in the hope that it will be useful, but WITHOUT
 *   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 *   for more details.
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software Foundation,
 *   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

declare(strict_types=1);

namespace Brainworxx\Krexx\Analyse\Callback\Iterate;

use Brainworxx\Krexx\Analyse\Declaration\MethodDeclaration;
use Brainworxx\Krexx\Analyse\Getter\AbstractGetter;
use Brainworxx\Krexx\Analyse\Getter\ByMethodName;
use Brainworxx\Krexx\Analyse\Getter\ByRegExContainer;
use Brainworxx\Krexx\Analyse\Getter\ByRegExDelegate;
use Brainworxx\Krexx\Analyse\Getter\ByRegExProperty;
use Brainworxx\Krexx\Analyse\Model;
use ReflectionMethod;
use Brainworxx\Krexx\Analyse\Comment\Methods;
use Brainworxx\Krexx\Service\Factory\Pool;
use Brainworxx\Krexx\Analyse\Callback\AbstractCallback;
use Brainworxx\Krexx\Analyse\Callback\CallbackConstInterface;
use Brainworxx\Krexx\Analyse\Code\CodegenConstInterface;
use Brainworxx\Krexx\Analyse\Code\ConnectorsConstInterface;

/**
 * Getter method analysis methods.
 *
 * @uses array normalGetter
 *   The list of all reflection methods we are analysing, hosting the
 *   get methods starting with 'get'
 * @uses array isGetter
 *   The list of all reflection methods we are analysing, hosting the
 *   get methods starting with 'is'
 * @uses array hasGetter
 *   The list of all reflection methods we are analysing, hosting the
 *   get methods starting with 'has'
 * @uses \Brainworxx\Krexx\Service\Reflection\ReflectionClass ref
 *   A reflection class of the object we are analysing.
 * @uses object data
 *   The object we are currently analysing
 * @uses string currentPrefix
 *   The current prefix we are analysing (get, is, has).
 *   Does not get set from the outside.
 * @uses mixed value
 *   Store the retrieved value from the getter analysis here and give
 *   event subscribers the opportunity to do something with it.
 */
class ThroughGetter extends AbstractCallback implements
    CallbackConstInterface,
    CodegenConstInterface,
    ConnectorsConstInterface
{
    /**
     * The parameter name of the prefix we ara analysing.
     *
     * @var string
     */
    public const CURRENT_PREFIX = 'currentPrefix';

    /**
     * Here we memorize how deep we are inside the current deep analysis.
     *
     * @var int
     */
    protected int $deep = 0;

    /**
     * These analysers will take a look at the getter.
     *
     * @var \Brainworxx\Krexx\Analyse\Getter\AbstractGetter[]
     */
    protected array $getterAnalyser;

    /**
     * Class for the comment analysis.
     *
     * @var \Brainworxx\Krexx\Analyse\Comment\Methods
     */
    protected Methods $commentAnalysis;

    /**
     * The method declaration retriever.
     *
     * @var \Brainworxx\Krexx\Analyse\Declaration\MethodDeclaration
     */
    protected MethodDeclaration $methodDeclaration;

    /**
     * Injects the pool and initializes the comment analysis.
     *
     * @param \Brainworxx\Krexx\Service\Factory\Pool $pool
     */
    public function __construct(Pool $pool)
    {
        parent::__construct($pool);
        $this->commentAnalysis = $this->pool->createClass(Methods::class);
        $this->getterAnalyser = [
            $this->pool->createClass(ByMethodName::class),
            $this->pool->createClass(ByRegExProperty::class),
            $this->pool->createClass(ByRegExContainer::class),
            $this->pool->createClass(ByRegExDelegate::class)
        ];
        $this->methodDeclaration = $this->pool->createClass(MethodDeclaration::class);
    }

    /**
     * Try to get the possible result of all getter methods.
     *
     * @return string
     *   The generated markup.
     */
    public function callMe(): string
    {
        $output = $this->dispatchStartEvent();

        if (!empty($this->parameters[static::PARAM_NORMAL_GETTER])) {
            $this->parameters[static::CURRENT_PREFIX] = 'get';
            $output .= $this->goThroughMethodList($this->parameters[static::PARAM_NORMAL_GETTER]);
        }

        if (!empty($this->parameters[static::PARAM_IS_GETTER])) {
            $this->parameters[static::CURRENT_PREFIX] = 'is';
            $output .= $this->goThroughMethodList($this->parameters[static::PARAM_IS_GETTER]);
        }

        if (!empty($this->parameters[static::PARAM_HAS_GETTER])) {
            $this->parameters[static::CURRENT_PREFIX] = 'has';
            $output .= $this->goThroughMethodList($this->parameters[static::PARAM_HAS_GETTER]);
        }

        return $output;
    }

    /**
     * Iterating through a list of reflection methods.
     *
     * @param \ReflectionMethod[] $methodList
     *   The list of methods we are going through, consisting of \ReflectionMethod
     *
     * @return string
     *   The generated DOM.
     */
    protected function goThroughMethodList(array $methodList): string
    {
        $output = '';
        foreach ($methodList as $reflectionMethod) {
            // Back to level 0, we reset the deep counter.
            $this->deep = 0;

            // Now we have three possible outcomes:
            // 1.) We have an actual value
            // 2.) We got NULL as a value
            // 3.) We were unable to get any info at all.
            /** @var Model $model */
            $model = $this->pool->createClass(Model::class)
                ->setName($reflectionMethod->getName())
                ->setCodeGenType(static::CODEGEN_TYPE_PUBLIC);
            $this->assignMetaDataToJson($model, $reflectionMethod);

            // We need to decide if we are handling static getters.
            if ($reflectionMethod->isStatic()) {
                $model->setConnectorType(static::CONNECTOR_STATIC_METHOD);
            } else {
                $model->setConnectorType(static::CONNECTOR_METHOD);
            }

            // Get ourselves a possible return value
            $output .= $this->retrievePropertyValue(
                $reflectionMethod,
                $this->dispatchEventWithModel(
                    __FUNCTION__ . static::EVENT_MARKER_END,
                    $model
                )
            );
        }

        return $output;
    }

    /**
     * We assign the metadata (comments and declaration) to the model.
     *
     * @param \Brainworxx\Krexx\Analyse\Model $model
     *   The model so far.
     * @param \ReflectionMethod $reflectionMethod
     *   Reflection of the method that we are analysing.
     */
    protected function assignMetaDataToJson(Model $model, ReflectionMethod $reflectionMethod): void
    {
        $comments = $this->commentAnalysis
            ->getComment($reflectionMethod, $this->parameters[static::PARAM_REF]);
        $declaration = nl2br($this->methodDeclaration->retrieveDeclaration($reflectionMethod));
        $messages = $this->pool->messages;
        $model->addToJson($messages->getHelp('metaMethodComment'), nl2br($comments))
            ->addToJson($messages->getHelp('metaDeclaredIn'), $declaration);
    }

    /**
     * Try to get a possible return value and render the result.
     *
     * @param \ReflectionMethod $reflectionMethod
     *   A reflection ot the method we are analysing
     * @param Model $model
     *   The model so far.
     *
     * @return string
     *   The rendered markup.
     */
    protected function retrievePropertyValue(ReflectionMethod $reflectionMethod, Model $model): string
    {
        $this->resetParameters($reflectionMethod);
        /** @var \Brainworxx\Krexx\Service\Reflection\ReflectionClass $reflectionClass */
        $reflectionClass = $this->parameters[static::PARAM_REF];
        $currentPrefix = $this->parameters[static::CURRENT_PREFIX];
        foreach ($this->getterAnalyser as $analyser) {
            $value = $analyser->retrieveIt($reflectionMethod, $reflectionClass, $currentPrefix);
            if ($analyser->hasResult()) {
                $this->prepareParameters($value, $analyser, $reflectionMethod);
                $this->prepareModel($model, $value);
                break;
            }
        }

        $this->dispatchEventWithModel(__FUNCTION__ . '::resolving', $model);

        if ($this->parameters[static::PARAM_ADDITIONAL][static::PARAM_NOTHING_FOUND]) {
            $messages = $this->pool->messages;
            // Found nothing  :-(
            // We literally have no info. We need to tell the user.
            // We render this right away, without any routing.
            return $this->pool->render->renderExpandableChild($this->dispatchEventWithModel(
                __FUNCTION__ . static::EVENT_MARKER_END,
                $model->setType($messages->getHelp('getterValueUnknown'))
                    ->setNormal($messages->getHelp('getterValueUnknown'))
                    ->addToJson($messages->getHelp('metaHint'), $messages->getHelp('getterUnknown'))
            ));
        }

        return $this->pool->routing->analysisHub(
            $this->dispatchEventWithModel(__FUNCTION__ . static::EVENT_MARKER_END, $model)
        );
    }

    /**
     * Prepare the model with the retrieved value.
     *
     * @param \Brainworxx\Krexx\Analyse\Model $model
     *   The model, so far.
     * @param mixed $value
     *   The retrieved possible value. Can be anything.
     */
    protected function prepareModel(Model $model, $value): void
    {
        $model->setData($value);
        if ($value === null) {
            // A NULL value might mean that the values does not
            // exist, until the getter computes it.
            $model->addToJson(
                $this->pool->messages->getHelp('metaHint'),
                $this->pool->messages->getHelp('getterNull')
            );
        }
    }

    /**
     * @param mixed $value
     *   The possible value that we retrieved.
     * @param \Brainworxx\Krexx\Analyse\Getter\AbstractGetter $analyser
     *   The analyser that we used.
     * @param \ReflectionMethod $reflectionMethod
     *   Reflection of the method that we are analysing.
     */
    protected function prepareParameters($value, AbstractGetter $analyser, ReflectionMethod $reflectionMethod): void
    {
        $this->parameters[static::PARAM_ADDITIONAL] = [
            static::PARAM_NOTHING_FOUND => false,
            static::PARAM_VALUE => $value,
            static::PARAM_REFLECTION_PROPERTY => $analyser->getReflectionProperty(),
            static::PARAM_REFLECTION_METHOD => $reflectionMethod
        ];
    }

    /**
     * Reset the parameters for every getter.
     *
     * We do this for the eventsystem, so a listener can gete additional data
     * from the current analysis process. Or the listener can inject stuff
     * here.
     *
     * @param \ReflectionMethod $reflectionMethod
     * @return void
     */
    protected function resetParameters(ReflectionMethod $reflectionMethod)
    {
        $this->parameters[static::PARAM_ADDITIONAL] = [
            static::PARAM_NOTHING_FOUND => true,
            static::PARAM_VALUE => null,
            static::PARAM_REFLECTION_PROPERTY => null,
            static::PARAM_REFLECTION_METHOD => $reflectionMethod
        ];
    }
}