Completed
Pull Request — 5.6 (#2830)
by Jeroen
14:14
created

src/Kunstmaan/SeoBundle/Twig/SeoTwigExtension.php (1 issue)

Check that @param annotations have the correct type.

Documentation Informational

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Kunstmaan\SeoBundle\Twig;
4
5
use Doctrine\ORM\EntityManager;
6
use Kunstmaan\AdminBundle\Entity\AbstractEntity;
7
use Kunstmaan\NodeBundle\Entity\AbstractPage;
8
use Kunstmaan\SeoBundle\Entity\Seo;
9
use Psr\Cache\CacheItemPoolInterface;
10
use Twig\Environment;
11
use Twig\Extension\AbstractExtension;
12
use Twig\TwigFunction;
13
14
/**
15
 * Twig extensions for Seo
16
 *
17
 * @final since 5.4
18
 */
19
class SeoTwigExtension extends AbstractExtension
20
{
21
    /**
22
     * @var EntityManager
23
     */
24
    protected $em;
25
26
    /**
27
     * Website title defined in your parameters
28
     *
29
     * @var string
30
     */
31
    private $websiteTitle;
32
33
    /**
34
     * Saves querying the db multiple times, if you happen to use any of the defined
35
     * functions more than once in your templates
36
     *
37
     * @var array
38
     */
39
    private $seoCache = [];
40
41
    /**
42
     * @var CacheItemPoolInterface
43
     */
44
    private $requestCache;
45
46
    public function __construct(EntityManager $em)
47
    {
48
        $this->em = $em;
49
    }
50
51
    /**
52
     * Returns a list of functions to add to the existing list.
53
     *
54
     * @return array An array of functions
55
     */
56
    public function getFunctions()
57
    {
58
        return [
59
            new TwigFunction('render_seo_metadata_for', [$this, 'renderSeoMetadataFor'], ['is_safe' => ['html'], 'needs_environment' => true]),
60
            new TwigFunction('get_seo_for', [$this, 'getSeoFor']),
61
            new TwigFunction('get_title_for', [$this, 'getTitleFor']),
62
            new TwigFunction('get_title_for_page_or_default', [$this, 'getTitleForPageOrDefault']),
63
            new TwigFunction('get_absolute_url', [$this, 'getAbsoluteUrl']),
64
            new TwigFunction('get_image_dimensions', [$this, 'getImageDimensions']),
65
        ];
66
    }
67
68
    /**
69
     * Validates the $url value as URL (according to » http://www.faqs.org/rfcs/rfc2396), optionally with required components.
70
     * It will just return the url if it's valid. If it starts with '/', the $host will be prepended.
71
     *
72
     * @param string $url
73
     * @param string $host
0 ignored issues
show
Should the type for parameter $host not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
74
     *
75
     * @return string
76
     */
77
    public function getAbsoluteUrl($url, $host = null)
78
    {
79
        $validUrl = filter_var($url, FILTER_VALIDATE_URL);
80
        $host = rtrim($host, '/');
81
82
        if (!$validUrl === false) {
83
            // The url is valid
84
            return $url;
85
        }
86
87
        // Prepend with $host if $url starts with "/"
88
        if (strpos($url, '/') === 0) {
89
            return $url = $host . $url;
90
        }
91
92
        return false;
93
    }
94
95
    /**
96
     * @return Seo
97
     */
98
    public function getSeoFor(AbstractPage $entity)
99
    {
100
        $key = md5(\get_class($entity) . $entity->getId());
101
102
        if (!\array_key_exists($key, $this->seoCache)) {
103
            $seo = $this->em->getRepository(Seo::class)->findOrCreateFor($entity);
104
            $this->seoCache[$key] = $seo;
105
        }
106
107
        return $this->seoCache[$key];
108
    }
109
110
    /**
111
     * The first value that is not null or empty will be returned.
112
     *
113
     * @param AbstractPage $entity the entity for which you want the page title
114
     *
115
     * @return string The page title. Will look in the SEO meta first, then the NodeTranslation, then the page.
116
     */
117
    public function getTitleFor(AbstractPage $entity)
118
    {
119
        $arr = [];
120
121
        $arr[] = $this->getSeoTitle($entity);
122
123
        $arr[] = $entity->getTitle();
124
125
        return $this->getPreferredValue($arr);
126
    }
127
128
    /**
129
     * @param AbstractPage $entity
130
     * @param string|null  $default if given we'll return this text if no SEO title was found
131
     *
132
     * @return string
133
     */
134
    public function getTitleForPageOrDefault(AbstractPage $entity = null, $default = null)
135
    {
136
        if (\is_null($entity)) {
137
            return $default;
138
        }
139
140
        $arr = [];
141
142
        $arr[] = $this->getSeoTitle($entity);
143
144
        $arr[] = $default;
145
146
        $arr[] = $entity->getTitle();
147
148
        return $this->getPreferredValue($arr);
149
    }
150
151
    /**
152
     * @param AbstractEntity $entity      The entity
153
     * @param mixed          $currentNode The current node
154
     * @param string         $template    The template
155
     *
156
     * @return string
157
     */
158
    public function renderSeoMetadataFor(Environment $environment, AbstractEntity $entity, $currentNode = null, $template = '@KunstmaanSeo/SeoTwigExtension/metadata.html.twig')
159
    {
160
        $seo = $this->getSeoFor($entity);
161
        $template = $environment->load($template);
162
163
        return $template->render(
164
            [
165
                'seo' => $seo,
166
                'entity' => $entity,
167
                'currentNode' => $currentNode,
168
            ]
169
        );
170
    }
171
172
    /**
173
     * @return string
174
     */
175
    protected function getPreferredValue(array $values)
176
    {
177
        foreach ($values as $v) {
178
            if (!\is_null($v) && !empty($v)) {
179
                return $v;
180
            }
181
        }
182
183
        return '';
184
    }
185
186
    /**
187
     * @param AbstractPage $entity
188
     *
189
     * @return string|null
190
     */
191
    private function getSeoTitle(AbstractPage $entity = null)
192
    {
193
        if (\is_null($entity)) {
194
            return null;
195
        }
196
197
        $seo = $this->getSeoFor($entity);
198
        if (!\is_null($seo)) {
199
            $title = $seo->getMetaTitle();
200
            if (!empty($title)) {
201
                return str_replace('%websitetitle%', $this->getWebsiteTitle(), $title);
202
            }
203
        }
204
205
        return null;
206
    }
207
208
    /**
209
     * Gets the Website title defined in your parameters.
210
     *
211
     * @return string
212
     */
213
    public function getWebsiteTitle()
214
    {
215
        return $this->websiteTitle;
216
    }
217
218
    /**
219
     * Sets the Website title defined in your parameters.
220
     *
221
     * @param string $websiteTitle the website title
222
     *
223
     * @return self
224
     */
225
    public function setWebsiteTitle($websiteTitle)
226
    {
227
        $this->websiteTitle = $websiteTitle;
228
229
        return $this;
230
    }
231
232
    /**
233
     * @param $src
234
     *
235
     * @return array
236
     */
237
    public function getImageDimensions($src)
238
    {
239
        list($width, $height) = $this->getImageSize($src);
240
241
        return ['width' => $width, 'height' => $height];
242
    }
243
244
    public function setRequestCache(CacheItemPoolInterface $cacheService)
245
    {
246
        $this->requestCache = $cacheService;
247
    }
248
249
    /**
250
     * @return CacheItemPoolInterface
251
     */
252
    public function getRequestCache()
253
    {
254
        return $this->requestCache;
255
    }
256
257
    private function getImageSize($src)
258
    {
259
        try {
260
            $cache = $this->getRequestCache();
261
            if (null === $cache) {
262
                return getimagesize($src);
263
            }
264
265
            $cachedImageSizes = $cache->getItem(md5($src));
266
            if (!$cachedImageSizes->isHit()) {
267
                $sizes = getimagesize($src);
268
269
                $cachedImageSizes->set($sizes);
270
                $cache->save($cachedImageSizes);
271
            }
272
273
            return $cachedImageSizes->get();
274
        } catch (\Exception $e) {
275
            return [null, null];
276
        }
277
    }
278
}
279