Completed
Push — 2.0 ( 1bba9f...1e5061 )
by David
9s
created

ChainRenderer::getRendererDebugMessage()   B

Complexity

Conditions 11
Paths 15

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 39
rs 7.3166
c 0
b 0
f 0
nc 15
cc 11
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * Copyright (c) 2013 David Negrier
4
 *
5
 * See the file LICENSE.txt for copying permission.
6
 */
7
8
namespace Mouf\Html\Renderer;
9
10
use Psr\Container\ContainerInterface;
11
use Psr\SimpleCache\CacheInterface;
12
13
/**
14
 * This class is a renderer that renders objects using other renderers.
15
 * This renderer will automatically detect the renderers to be included.
16
 * They must extend the ChainableRendererInterface interface.
17
 *
18
 * @author David Négrier <[email protected]>
19
 */
20
class ChainRenderer implements CanSetTemplateRendererInterface
21
{
22
23
    /**
24
     * @var ChainableRendererInterface|null
25
     */
26
    private $templateRenderer;
27
    /**
28
     * @var ChainableRendererInterface[]
29
     */
30
    private $packageRenderers = [];
31
    /**
32
     * @var ChainableRendererInterface[]
33
     */
34
    private $customRenderers = [];
35
36
    private $cacheService;
37
38
    private $initDone = false;
39
    /**
40
     * @var string[]
41
     */
42
    private $customRendererInstanceNames;
43
    /**
44
     * @var string
45
     */
46
    private $templateRendererInstanceName;
47
    /**
48
     * @var string[]
49
     */
50
    private $packageRendererInstanceNames;
51
    /**
52
     * @var string
53
     */
54
    private $uniqueName;
55
    /**
56
     * @var ContainerInterface
57
     */
58
    private $container;
59
60
    /**
61
     *
62
     * @param string[] $customRendererInstanceNames An array of names of custom renderers (container identifiers)
63
     * @param string[] $packageRendererInstanceNames An array of names of package renderers (container identifiers)
64
     * @param CacheInterface $cacheService This service is used to speed up the mapping between the object and the template.
65
     * @param string $uniqueName The unique name for this instance (used for caching purpose)
66
     */
67
    public function __construct(ContainerInterface $container, array $customRendererInstanceNames, array $packageRendererInstanceNames, CacheInterface $cacheService, string $uniqueName)
68
    {
69
        $this->container = $container;
70
        $this->customRendererInstanceNames = $customRendererInstanceNames;
71
        $this->packageRendererInstanceNames = $packageRendererInstanceNames;
72
        $this->cacheService = $cacheService;
73
        $this->uniqueName = $uniqueName;
74
    }
75
76
    /**
77
     * (non-PHPdoc)
78
     * @see \Mouf\Html\Renderer\RendererInterface::render()
79
     */
80
    public function render($object, string $context = null): void
81
    {
82
        $renderer = $this->getRenderer($object, $context);
83
        if ($renderer == null) {
84
            throw new NoRendererFoundException("Renderer not found. Unable to find renderer for object of class '".get_class($object)."'. Path tested: ".$this->getRendererDebugMessage($object, $context));
85
        }
86
        $renderer->render($object, $context);
87
    }
88
89
    /**
90
     * @param object $object
91
     * @param string|null $context
92
     * @return ChainableRendererInterface|null
93
     */
94
    private function getRenderer($object, string $context = null): ?ChainableRendererInterface
95
    {
96
        $cacheKey = "chainRendererByClass_".md5($this->uniqueName."/".$this->templateRendererInstanceName."/".get_class($object)."/".$context);
97
98
        $cachedInstanceName = $this->cacheService->get($cacheKey);
99
        if ($cachedInstanceName !== null) {
100
            return $this->container->get($cachedInstanceName);
101
        }
102
103
        $this->initRenderersList();
104
105
        $isCachable = true;
106
        $foundRenderer = null;
107
        $source = null;
0 ignored issues
show
Unused Code introduced by
$source is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
108
        $foundInstanceName = null;
109
110
        do {
111
            foreach ($this->customRenderers as $instanceName => $renderer) {
112
                $result = $renderer->canRender($object, $context);
113
                if ($result === ChainableRendererInterface::CAN_RENDER_OBJECT || $result === ChainableRendererInterface::CANNOT_RENDER_OBJECT) {
114
                    $isCachable = false;
115
                }
116
                if ($result === ChainableRendererInterface::CAN_RENDER_OBJECT || $result === ChainableRendererInterface::CAN_RENDER_CLASS) {
117
                    $foundRenderer = $renderer;
118
                    $foundInstanceName = $instanceName;
119
                    break 2;
120
                }
121
            }
122
123
            if ($this->templateRendererInstanceName && !$this->templateRenderer) {
124
                $this->templateRenderer = $this->container->get($this->templateRendererInstanceName);
125
            }
126
            if ($this->templateRenderer) {
127
                $result = $this->templateRenderer->canRender($object, $context);
128
                if ($result === ChainableRendererInterface::CAN_RENDER_OBJECT || $result === ChainableRendererInterface::CANNOT_RENDER_OBJECT) {
129
                    $isCachable = false;
130
                }
131
                if ($result === ChainableRendererInterface::CAN_RENDER_OBJECT || $result === ChainableRendererInterface::CAN_RENDER_CLASS) {
132
                    $foundRenderer = $this->templateRenderer;
133
                    break;
134
                }
135
            }
136
137
            foreach ($this->packageRenderers as $instanceName => $renderer) {
138
                $result = $renderer->canRender($object, $context);
139
                if ($result === ChainableRendererInterface::CAN_RENDER_OBJECT || $result === ChainableRendererInterface::CANNOT_RENDER_OBJECT) {
140
                    $isCachable = false;
141
                }
142
                if ($result === ChainableRendererInterface::CAN_RENDER_OBJECT || $result === ChainableRendererInterface::CAN_RENDER_CLASS) {
143
                    $foundRenderer = $renderer;
144
                    $foundInstanceName = $instanceName;
145
                    break 2;
146
                }
147
            }
148
        } while (false);
149
150
        if ($isCachable && $foundRenderer) {
151
            $this->cacheService->set($cacheKey, $foundInstanceName);
152
        }
153
154
        return $foundRenderer;
155
    }
156
157
    /**
158
     * Returns a string explaining the steps done to find the renderer.
159
     *
160
     * @param  object $object
161
     * @param  string $context
162
     * @return string
163
     */
164
    private function getRendererDebugMessage($object, string $context = null): string
165
    {
166
        $debugMessage = '';
167
168
        $this->initRenderersList();
169
170
        do {
171
            foreach ($this->customRenderers as $renderer) {
172
                /* @var $renderer ChainableRendererInterface */
173
174
                $debugMessage .= $renderer->debugCanRender($object, $context);
175
                $result = $renderer->canRender($object, $context);
176
                if ($result === ChainableRendererInterface::CAN_RENDER_OBJECT || $result === ChainableRendererInterface::CAN_RENDER_CLASS) {
177
                    break 2;
178
                }
179
            }
180
181
            /* @var $renderer ChainableRendererInterface */
182
            if ($this->templateRenderer) {
183
                $debugMessage .= $this->templateRenderer->debugCanRender($object, $context);
184
                $result = $this->templateRenderer->canRender($object, $context);
185
                if ($result === ChainableRendererInterface::CAN_RENDER_OBJECT || $result === ChainableRendererInterface::CAN_RENDER_CLASS) {
186
                    break;
187
                }
188
            }
189
190
            foreach ($this->packageRenderers as $renderer) {
191
                /* @var $renderer ChainableRendererInterface */
192
193
                $debugMessage .= $renderer->debugCanRender($object, $context);
194
                $result = $renderer->canRender($object, $context);
195
                if ($result === ChainableRendererInterface::CAN_RENDER_OBJECT || $result === ChainableRendererInterface::CAN_RENDER_CLASS) {
196
                    break 2;
197
                }
198
            }
199
        } while (false);
200
        
201
        return $debugMessage;
202
    }
203
    
204
    /**
205
     * Initializes the renderers list (from cache if available)
206
     */
207
    private function initRenderersList(): void
208
    {
209
        if (!$this->initDone) {
210
            foreach ($this->customRendererInstanceNames as $instanceName) {
211
                $this->customRenderers[$instanceName] = $this->container->get($instanceName);
212
            }
213
            foreach ($this->packageRendererInstanceNames as $instanceName) {
214
                $this->packageRenderers[$instanceName] = $this->container->get($instanceName);
215
            }
216
217
            // Note: We ignore template renderers on purpose.
218
            $this->initDone = true;
219
        }
220
    }
221
222
    /**
223
     * Sets the renderer associated to the template.
224
     * There should be only one if these renderers.
225
     * It is the role of the template to subscribe to this renderer.
226
     *
227
     * @param string $templateRendererInstanceName The name of the template renderer in the container
228
     */
229
    public function setTemplateRendererInstanceName(string $templateRendererInstanceName): void
230
    {
231
        $this->templateRendererInstanceName = $templateRendererInstanceName;
232
    }
233
}
234