| Total Complexity | 52 | 
| Total Lines | 319 | 
| Duplicated Lines | 0 % | 
| Changes | 0 | ||
Complex classes like EmbedShortcodeProvider often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use EmbedShortcodeProvider, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 28 | class EmbedShortcodeProvider implements ShortcodeHandler  | 
            ||
| 29 | { | 
            ||
| 30 | |||
| 31 | /**  | 
            ||
| 32 | * Gets the list of shortcodes provided by this handler  | 
            ||
| 33 | *  | 
            ||
| 34 | * @return mixed  | 
            ||
| 35 | */  | 
            ||
| 36 | public static function get_shortcodes()  | 
            ||
| 39 | }  | 
            ||
| 40 | |||
| 41 | /**  | 
            ||
| 42 | * Embed shortcode parser from Oembed. This is a temporary workaround.  | 
            ||
| 43 | * Oembed class has been replaced with the Embed external service.  | 
            ||
| 44 | *  | 
            ||
| 45 | * @param array $arguments  | 
            ||
| 46 | * @param string $content  | 
            ||
| 47 | * @param ShortcodeParser $parser  | 
            ||
| 48 | * @param string $shortcode  | 
            ||
| 49 | * @param array $extra  | 
            ||
| 50 | *  | 
            ||
| 51 | * @return string  | 
            ||
| 52 | */  | 
            ||
| 53 | public static function handle_shortcode($arguments, $content, $parser, $shortcode, $extra = [])  | 
            ||
| 54 |     { | 
            ||
| 55 | // Get service URL  | 
            ||
| 56 |         if (!empty($content)) { | 
            ||
| 57 | $serviceURL = $content;  | 
            ||
| 58 |         } elseif (!empty($arguments['url'])) { | 
            ||
| 59 | $serviceURL = $arguments['url'];  | 
            ||
| 60 |         } else { | 
            ||
| 61 | return '';  | 
            ||
| 62 | }  | 
            ||
| 63 | |||
| 64 | $class = $arguments['class'] ?? '';  | 
            ||
| 65 | $width = $arguments['width'] ?? '';  | 
            ||
| 66 | $height = $arguments['height'] ?? '';  | 
            ||
| 67 | |||
| 68 | // Try to use cached result  | 
            ||
| 69 | $cache = static::getCache();  | 
            ||
| 70 | $key = static::deriveCacheKey($serviceURL, $class, $width, $height);  | 
            ||
| 71 |         try { | 
            ||
| 72 |             if (false && $cache->has($key)) { | 
            ||
| 73 | return $cache->get($key);  | 
            ||
| 74 | }  | 
            ||
| 75 |         } catch (InvalidArgumentException $e) { | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 76 | }  | 
            ||
| 77 | |||
| 78 | // See https://github.com/oscarotero/Embed#example-with-all-options for service arguments  | 
            ||
| 79 | $serviceArguments = [];  | 
            ||
| 80 |         if (!empty($arguments['width'])) { | 
            ||
| 81 | $serviceArguments['min_image_width'] = $arguments['width'];  | 
            ||
| 82 | }  | 
            ||
| 83 |         if (!empty($arguments['height'])) { | 
            ||
| 84 | $serviceArguments['min_image_height'] = $arguments['height'];  | 
            ||
| 85 | }  | 
            ||
| 86 | |||
| 87 | /** @var EmbedContainer $embeddable */  | 
            ||
| 88 | $embeddable = Injector::inst()->create(Embeddable::class, $serviceURL);  | 
            ||
| 89 | |||
| 90 | // Only EmbedContainer is currently supported  | 
            ||
| 91 |         if (!($embeddable instanceof EmbedContainer)) { | 
            ||
| 92 |             throw new \RuntimeException('Emeddable must extend EmbedContainer'); | 
            ||
| 93 | }  | 
            ||
| 94 | |||
| 95 | $crawler = Injector::inst()->get(Crawler::class);  | 
            ||
| 96 | $embeddable->setCrawler($crawler);  | 
            ||
| 97 | |||
| 98 |         if (!empty($serviceArguments)) { | 
            ||
| 99 | $embeddable->setOptions(array_merge($serviceArguments, (array) $embeddable->getOptions()));  | 
            ||
| 100 | }  | 
            ||
| 101 | |||
| 102 | // Process embed  | 
            ||
| 103 |         try { | 
            ||
| 104 | // this will trigger a request/response which will then be cached within $embeddable  | 
            ||
| 105 | $embeddable->getExtractor();  | 
            ||
| 106 |         } catch (NetworkException | RequestException $e) { | 
            ||
| 107 | $message = (Director::isDev())  | 
            ||
| 108 | ? $e->getMessage()  | 
            ||
| 109 | : _t(__CLASS__ . '.INVALID_URL', 'There was a problem loading the media.');  | 
            ||
| 110 | |||
| 111 | $attr = [  | 
            ||
| 112 | 'class' => 'ss-media-exception embed'  | 
            ||
| 113 | ];  | 
            ||
| 114 | |||
| 115 | $result = HTML::createTag(  | 
            ||
| 116 | 'div',  | 
            ||
| 117 | $attr,  | 
            ||
| 118 |                 HTML::createTag('p', [], $message) | 
            ||
| 119 | );  | 
            ||
| 120 | return $result;  | 
            ||
| 121 | }  | 
            ||
| 122 | |||
| 123 | // Convert embed object into HTML  | 
            ||
| 124 | $html = static::embeddableToHtml($embeddable, $arguments);  | 
            ||
| 125 | // Fallback to link to service  | 
            ||
| 126 |         if (!$html) { | 
            ||
| 127 | $result = static::linkEmbed($arguments, $serviceURL, $serviceURL);  | 
            ||
| 128 | }  | 
            ||
| 129 | // Cache result  | 
            ||
| 130 |         if ($html) { | 
            ||
| 131 |             try { | 
            ||
| 132 | $cache->set($key, $html);  | 
            ||
| 133 |             } catch (InvalidArgumentException $e) { | 
            ||
| 134 | }  | 
            ||
| 135 | }  | 
            ||
| 136 | return $html;  | 
            ||
| 137 | }  | 
            ||
| 138 | |||
| 139 | public static function embeddableToHtml(Embeddable $embeddable, array $arguments): string  | 
            ||
| 140 |     { | 
            ||
| 141 | // Only EmbedContainer is supported  | 
            ||
| 142 |         if (!($embeddable instanceof EmbedContainer)) { | 
            ||
| 143 | return '';  | 
            ||
| 144 | }  | 
            ||
| 145 | $extractor = $embeddable->getExtractor();  | 
            ||
| 146 | $type = $embeddable->getType();  | 
            ||
| 147 |         if ($type === 'video' || $type === 'rich') { | 
            ||
| 148 | // Attempt to inherit width (but leave height auto)  | 
            ||
| 149 |             if (empty($arguments['width']) && $embeddable->getWidth()) { | 
            ||
| 150 | $arguments['width'] = $embeddable->getWidth();  | 
            ||
| 151 | }  | 
            ||
| 152 | return static::videoEmbed($arguments, $extractor->code->html);  | 
            ||
| 153 | }  | 
            ||
| 154 |         if ($type === 'photo') { | 
            ||
| 155 | return static::photoEmbed($arguments, (string) $extractor->url);  | 
            ||
| 156 | }  | 
            ||
| 157 |         if ($type === 'link') { | 
            ||
| 158 | return static::linkEmbed($arguments, (string) $extractor->url, $extractor->title);  | 
            ||
| 159 | }  | 
            ||
| 160 | return '';  | 
            ||
| 161 | }  | 
            ||
| 162 | |||
| 163 | /**  | 
            ||
| 164 | * @param Adapter $embed  | 
            ||
| 165 | * @param array $arguments Additional shortcode params  | 
            ||
| 166 | * @return string  | 
            ||
| 167 | * @deprecated 4.11..5.0 Use embeddableToHtml instead  | 
            ||
| 168 | */  | 
            ||
| 169 | public static function embedForTemplate($embed, $arguments)  | 
            ||
| 170 |     { | 
            ||
| 171 |         Deprecation::notice('4.11', 'Use embeddableToHtml() instead'); | 
            ||
| 172 |         switch ($embed->getType()) { | 
            ||
| 173 | case 'video':  | 
            ||
| 174 | case 'rich':  | 
            ||
| 175 | // Attempt to inherit width (but leave height auto)  | 
            ||
| 176 |                 if (empty($arguments['width']) && $embed->getWidth()) { | 
            ||
| 177 | $arguments['width'] = $embed->getWidth();  | 
            ||
| 178 | }  | 
            ||
| 179 | return static::videoEmbed($arguments, $embed->getCode());  | 
            ||
| 180 | case 'link':  | 
            ||
| 181 | return static::linkEmbed($arguments, $embed->getUrl(), $embed->getTitle());  | 
            ||
| 182 | case 'photo':  | 
            ||
| 183 | return static::photoEmbed($arguments, $embed->getUrl());  | 
            ||
| 184 | default:  | 
            ||
| 185 | return null;  | 
            ||
| 186 | }  | 
            ||
| 187 | }  | 
            ||
| 188 | |||
| 189 | /**  | 
            ||
| 190 | * Build video embed tag  | 
            ||
| 191 | *  | 
            ||
| 192 | * @param array $arguments  | 
            ||
| 193 | * @param string $content Raw HTML content  | 
            ||
| 194 | * @return string  | 
            ||
| 195 | */  | 
            ||
| 196 | protected static function videoEmbed($arguments, $content)  | 
            ||
| 197 |     { | 
            ||
| 198 | // Ensure outer div has given width (but leave height auto)  | 
            ||
| 199 |         if (!empty($arguments['width'])) { | 
            ||
| 200 | $arguments['style'] = 'width: ' . intval($arguments['width']) . 'px;';  | 
            ||
| 201 | }  | 
            ||
| 202 | |||
| 203 | // override iframe dimension attributes provided by webservice with ones specified in shortcode arguments  | 
            ||
| 204 |         foreach (['width', 'height'] as $attr) { | 
            ||
| 205 |             if (!($value = $arguments[$attr] ?? false)) { | 
            ||
| 206 | continue;  | 
            ||
| 207 | }  | 
            ||
| 208 |             foreach (['"', "'"] as $quote) { | 
            ||
| 209 | $rx = "/(<iframe .*?)$attr=$quote([0-9]+)$quote([^>]+>)/";  | 
            ||
| 210 |                 $content = preg_replace($rx, "$1{$attr}={$quote}{$value}{$quote}$3", $content); | 
            ||
| 211 | }  | 
            ||
| 212 | }  | 
            ||
| 213 | |||
| 214 | $data = [  | 
            ||
| 215 | 'Arguments' => $arguments,  | 
            ||
| 216 | 'Attributes' => static::buildAttributeListFromArguments($arguments, ['width', 'height', 'url', 'caption']),  | 
            ||
| 217 |             'Content' => DBField::create_field('HTMLFragment', $content) | 
            ||
| 218 | ];  | 
            ||
| 219 | |||
| 220 | return ArrayData::create($data)->renderWith(self::class . '_video')->forTemplate();  | 
            ||
| 221 | }  | 
            ||
| 222 | |||
| 223 | /**  | 
            ||
| 224 | * Build <a> embed tag  | 
            ||
| 225 | *  | 
            ||
| 226 | * @param array $arguments  | 
            ||
| 227 | * @param string $href  | 
            ||
| 228 | * @param string $title Default title  | 
            ||
| 229 | * @return string  | 
            ||
| 230 | */  | 
            ||
| 231 | protected static function linkEmbed($arguments, $href, $title)  | 
            ||
| 232 |     { | 
            ||
| 233 | $data = [  | 
            ||
| 234 | 'Arguments' => $arguments,  | 
            ||
| 235 | 'Attributes' => static::buildAttributeListFromArguments($arguments, ['width', 'height', 'url', 'caption']),  | 
            ||
| 236 | 'Href' => $href,  | 
            ||
| 237 | 'Title' => !empty($arguments['caption']) ? ($arguments['caption']) : $title  | 
            ||
| 238 | ];  | 
            ||
| 239 | |||
| 240 | return ArrayData::create($data)->renderWith(self::class . '_link')->forTemplate();  | 
            ||
| 241 | }  | 
            ||
| 242 | |||
| 243 | /**  | 
            ||
| 244 | * Build img embed tag  | 
            ||
| 245 | *  | 
            ||
| 246 | * @param array $arguments  | 
            ||
| 247 | * @param string $src  | 
            ||
| 248 | * @return string  | 
            ||
| 249 | */  | 
            ||
| 250 | protected static function photoEmbed($arguments, $src)  | 
            ||
| 251 |     { | 
            ||
| 252 | $data = [  | 
            ||
| 253 | 'Arguments' => $arguments,  | 
            ||
| 254 | 'Attributes' => static::buildAttributeListFromArguments($arguments, ['url']),  | 
            ||
| 255 | 'Src' => $src  | 
            ||
| 256 | ];  | 
            ||
| 257 | |||
| 258 | return ArrayData::create($data)->renderWith(self::class . '_photo')->forTemplate();  | 
            ||
| 259 | }  | 
            ||
| 260 | |||
| 261 | /**  | 
            ||
| 262 | * Build a list of HTML attributes from embed arguments - used to preserve backward compatibility  | 
            ||
| 263 | *  | 
            ||
| 264 |      * @deprecated 4.5.0 Use {$Arguments.name} directly in shortcode templates to access argument values | 
            ||
| 265 | * @param array $arguments List of embed arguments  | 
            ||
| 266 | * @param array $exclude List of attribute names to exclude from the resulting list  | 
            ||
| 267 | * @return ArrayList  | 
            ||
| 268 | */  | 
            ||
| 269 | private static function buildAttributeListFromArguments(array $arguments, array $exclude = []): ArrayList  | 
            ||
| 270 |     { | 
            ||
| 271 | $attributes = ArrayList::create();  | 
            ||
| 272 |         foreach ($arguments as $key => $value) { | 
            ||
| 273 |             if (in_array($key, $exclude)) { | 
            ||
| 274 | continue;  | 
            ||
| 275 | }  | 
            ||
| 276 | |||
| 277 | $attributes->push(ArrayData::create([  | 
            ||
| 278 | 'Name' => $key,  | 
            ||
| 279 | 'Value' => Convert::raw2att($value)  | 
            ||
| 280 | ]));  | 
            ||
| 281 | }  | 
            ||
| 282 | |||
| 283 | return $attributes;  | 
            ||
| 284 | }  | 
            ||
| 285 | |||
| 286 | /**  | 
            ||
| 287 | * @param ShortcodeParser $parser  | 
            ||
| 288 | * @param string $content  | 
            ||
| 289 | */  | 
            ||
| 290 | public static function flushCachedShortcodes(ShortcodeParser $parser, string $content): void  | 
            ||
| 313 | }  | 
            ||
| 314 | }  | 
            ||
| 315 | }  | 
            ||
| 316 | |||
| 317 | /**  | 
            ||
| 318 | * @return CacheInterface  | 
            ||
| 319 | */  | 
            ||
| 320 | private static function getCache(): CacheInterface  | 
            ||
| 321 |     { | 
            ||
| 322 | return Injector::inst()->get(CacheInterface::class . '.EmbedShortcodeProvider');  | 
            ||
| 323 | }  | 
            ||
| 324 | |||
| 325 | /**  | 
            ||
| 326 | * @param string $url  | 
            ||
| 327 | * @return string  | 
            ||
| 328 | */  | 
            ||
| 329 | private static function deriveCacheKey(string $url, string $class, string $width, string $height): string  | 
            ||
| 337 | ]));  | 
            ||
| 338 | }  | 
            ||
| 339 | |||
| 340 | /**  | 
            ||
| 341 | * @param string $str  | 
            ||
| 342 | * @return string  | 
            ||
| 343 | */  | 
            ||
| 344 | private static function cleanKeySegment(string $str): string  | 
            ||
| 347 | }  | 
            ||
| 348 | }  | 
            ||
| 349 |