PluginProbe
JCH Optimize / trunk
JCH Optimize vtrunk
6.0.2 6.0.1 trunk 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 1.2.1 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.1.0 2.10.0 2.2.0 2.2.1 All 58 releases
jch-optimize / lib / src / Service / Provider / Core.php

Core.php in JCH Optimize trunk, at lib/src/Service/Provider/Core.php

550 lines 20.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * JCH Optimize - Performs several front-end optimizations for fast downloads
5 *
6 * @package jchoptimize/core
7 * @author Samuel Marshall <samuel@jch-optimize.net>
8 * @copyright Copyright (c) 2022 Samuel Marshall / JCH Optimize
9 * @license GNU/GPLv3, or later. See LICENSE file
10 *
11 * If LICENSE file missing, see <http://www.gnu.org/licenses/>.
12 */
13
14 namespace JchOptimize\Core\Service\Provider;
15
16 use _JchOptimizeVendor\V91\Composer\CaBundle\CaBundle;
17 use _JchOptimizeVendor\V91\GuzzleHttp\Client;
18 use _JchOptimizeVendor\V91\GuzzleHttp\ClientInterface as GuzzleClientInterface;
19 use _JchOptimizeVendor\V91\GuzzleHttp\RequestOptions;
20 use _JchOptimizeVendor\V91\Joomla\DI\Container;
21 use _JchOptimizeVendor\V91\Joomla\DI\ServiceProviderInterface;
22 use _JchOptimizeVendor\V91\Joomla\Input\Input;
23 use _JchOptimizeVendor\V91\Laminas\Cache\Pattern\CallbackCache;
24 use _JchOptimizeVendor\V91\Laminas\Cache\Storage\StorageInterface;
25 use _JchOptimizeVendor\V91\Laminas\Cache\Storage\TaggableInterface;
26 use _JchOptimizeVendor\V91\Laminas\EventManager\LazyListener;
27 use _JchOptimizeVendor\V91\Laminas\EventManager\SharedEventManager;
28 use _JchOptimizeVendor\V91\Laminas\EventManager\SharedEventManagerInterface;
29 use _JchOptimizeVendor\V91\Psr\Http\Client\ClientInterface;
30 use _JchOptimizeVendor\V91\Psr\Log\LoggerInterface;
31 use Exception;
32 use JchOptimize\Core\Admin\HtmlCrawler;
33 use JchOptimize\Core\Admin\Icons;
34 use JchOptimize\Core\Admin\MultiSelectItems;
35 use JchOptimize\Core\Cdn\Cdn;
36 use JchOptimize\Core\Cloudflare\CloudflareApiClient;
37 use JchOptimize\Core\Cloudflare\CloudflareManager;
38 use JchOptimize\Core\Cloudflare\CloudflareVerifyService;
39 use JchOptimize\Core\Combiner;
40 use JchOptimize\Core\ConfigureHelper;
41 use JchOptimize\Core\Css\Callbacks\CorrectUrls;
42 use JchOptimize\Core\Css\Callbacks\ExtractCriticalCss;
43 use JchOptimize\Core\Css\Callbacks\FormatCss;
44 use JchOptimize\Core\Css\Callbacks\HandleAtRules;
45 use JchOptimize\Core\Css\Callbacks\PostProcessCriticalCss;
46 use JchOptimize\Core\Css\CssProcessor;
47 use JchOptimize\Core\Css\Sprite\Controller;
48 use JchOptimize\Core\Css\Sprite\Generator;
49 use JchOptimize\Core\Debug\JchProfiler;
50 use JchOptimize\Core\Debug\JchProfilerInterface;
51 use JchOptimize\Core\Debug\NullProfiler;
52 use JchOptimize\Core\FeatureHelpers\DynamicSelectors;
53 use JchOptimize\Core\FileUtils;
54 use JchOptimize\Core\Html\CacheManager;
55 use JchOptimize\Core\Html\CssLayout\CssLayoutPlanner;
56 use JchOptimize\Core\Html\FileSizeResolver;
57 use JchOptimize\Core\Html\FileSizeResolverInterface;
58 use JchOptimize\Core\Html\FilesManager;
59 use JchOptimize\Core\Html\HtmlManager;
60 use JchOptimize\Core\Html\HtmlProcessor;
61 use JchOptimize\Core\Html\JsLayout\JsLayoutPlanner;
62 use JchOptimize\Core\Html\SmartCombinePlanner;
63 use JchOptimize\Core\ImageAttributes;
64 use JchOptimize\Core\Model\CacheMaintainer;
65 use JchOptimize\Core\Optimize;
66 use JchOptimize\Core\PageCache\PageCache;
67 use JchOptimize\Core\Platform\CacheInterface;
68 use JchOptimize\Core\Platform\ExcludesInterface;
69 use JchOptimize\Core\Platform\HooksInterface;
70 use JchOptimize\Core\Platform\PathsInterface;
71 use JchOptimize\Core\Platform\ProfilerInterface;
72 use JchOptimize\Core\Platform\UtilityInterface;
73 use JchOptimize\Core\Preloads\Http2Preload;
74 use JchOptimize\Core\Preloads\Preconnector;
75 use JchOptimize\Core\Preloads\PreloadsResourceAggregator;
76 use JchOptimize\Core\Registry;
77 use JchOptimize\Core\SystemUri\SystemUriResolver;
78 use JchOptimize\Core\SystemUri\SystemUriResolverInterface;
79 use JchOptimize\Core\Uri\OriginClassifier;
80
81 use function defined;
82
83 use const JCH_PRO;
84
85 defined('_JCH_EXEC') or die('Restricted access');
86
87 class Core implements ServiceProviderInterface
88 {
89 public function register(Container $container): void
90 {
91 //Html
92 $container->share(CacheManager::class, [$this, 'getCacheManagerService']);
93 $container->share(FilesManager::class, [$this, 'getFilesManagerService']);
94 $container->share(HtmlManager::class, [$this, 'getHtmlManagerService']);
95 $container->share(HtmlProcessor::class, [$this, 'getHtmlProcessorService']);
96 //Css
97 $container->set(CssProcessor::class, [$this, 'getCssProcessorService']);
98 //Core
99 $container->share(Cdn::class, [$this, 'getCdnService']);
100 $container->share(Combiner::class, [$this, 'getCombinerService']);
101 $container->share(FileUtils::class, [$this, 'getFileUtilsService']);
102 $container->share(Http2Preload::class, [$this, 'getHttp2PreloadService']);
103 $container->share(Optimize::class, [$this, 'getOptimizeService']);
104 $container->share(Preconnector::class, [$this, 'getPreconnectorService']);
105 $container->share(ImageAttributes::class, [$this, 'getImageAttributesService']);
106 $container->share(OriginClassifier::class, [$this, 'getOriginClassifierService']);
107 $container->share(PreloadsResourceAggregator::class, [$this, 'getPreloadsResourceAggregatorService']);
108 $container->share(SystemUriResolverInterface::class, [$this, 'getSystemUriResolverService']);
109 //Admin
110 $container->share(HtmlCrawler::class, [$this, 'getHtmlCrawlerService']);
111 $container->share(Icons::class, [$this, 'getIconsService']);
112 $container->share(MultiSelectItems::class, [$this, 'getMultiSelectItemsService']);
113 //Sprite
114 $container->set(Generator::class, [$this, 'getSpriteGeneratorService']);
115 $container->set(Controller::class, [$this, 'getSpriteControllerService']);
116 //Vendor
117 $container->alias(GuzzleClientInterface::class, ClientInterface::class);
118 $container->share(ClientInterface::class, [$this, 'getClientInterfaceService']);
119 //MVC
120 $container->alias(Input::class, 'input')
121 ->share('input', [$this, 'getInputService']);
122 //Development
123 $container->share(ConfigureHelper::class, [$this, 'getConfigureHelperService']);
124 $container->share(JchProfilerInterface::class, [$this, 'getJchProfilerService']);
125 //Utility
126 $container->share(CacheMaintainer::class, [$this, 'getCacheMaintainerService']);
127 $container->share(CloudflareManager::class, [$this, 'getCloudflareManager']);
128 $container->share(CloudflareApiClient::class, [$this, 'getCloudflareApiClientService']);
129 $container->share(CloudflareVerifyService::class, [$this, 'getCloudflareVerifyServiceService']);
130 $container->share(FileSizeResolver::class, [$this, 'getFileSizeResolverService']);
131 $container->alias(FileSizeResolverInterface::class, FileSizeResolver::class);
132 $container->share(SmartCombinePlanner::class, fn(): SmartCombinePlanner => new SmartCombinePlanner());
133 //Feature Helpers
134 $container->share(DynamicSelectors::class, function (Container $container): DynamicSelectors {
135 return new DynamicSelectors(
136 $container,
137 $container->get(Registry::class)
138 );
139 });
140 //Domain
141 $container->share(CssLayoutPlanner::class, fn() => new CssLayoutPlanner());
142 $container->share(JsLayoutPlanner::class, fn() => new JsLayoutPlanner());
143
144 //Set up events management
145 /** @var SharedEventManager $sharedEvents */
146 $sharedEvents = $container->get(SharedEventManager::class);
147
148 $sharedEvents->attach(
149 HtmlManager::class,
150 'preProcessHtml',
151 new LazyListener([
152 /** @see HtmlManager::addCustomCss() */
153 'listener' => HtmlManager::class,
154 'method' => 'addCustomCss'
155 ], $container)
156 );
157
158 $sharedEvents->attach(
159 HtmlManager::class,
160 'preProcessHtml',
161 new LazyListener([
162 /** @see ImageAttributes::loadImageAttributesCss() */
163 'listener' => ImageAttributes::class,
164 'method' => 'loadImageAttributesCss'
165 ], $container)
166 );
167
168 $sharedEvents->attach(
169 HtmlManager::class,
170 'postProcessHtml',
171 new LazyListener([
172 /** @see Http2Preload::sendLinkHeaders() */
173 'listener' => Http2Preload::class,
174 'method' => 'sendLinkHeaders'
175 ], $container),
176 );
177
178 $sharedEvents->attach(
179 HtmlManager::class,
180 'postProcessHtml',
181 new LazyListener([
182 /** @see ConfigureHelper::loadNumElementsScript() */
183 'listener' => ConfigureHelper::class,
184 'method' => 'loadNumElementsScript'
185 ], $container),
186 200
187 );
188
189 $sharedEvents->attach(
190 HtmlManager::class,
191 'postProcessHtml',
192 new LazyListener([
193 /** @see ConfigureHelper::loadDynamicCssElementsScript() */
194 'listener' => ConfigureHelper::class,
195 'method' => 'loadDynamicCssElementsScript'
196 ], $container)
197 );
198
199 $sharedEvents->attach(
200 HtmlManager::class,
201 'postProcessHtml',
202 new LazyListener([
203 /** @see PreloadsResourceAggregator::injectHeadResources() */
204 'listener' => PreloadsResourceAggregator::class,
205 'method' => 'injectHeadResources'
206 ], $container)
207 );
208 }
209
210 /**
211 * @psalm-suppress InvalidArgument
212 */
213 public function getCacheManagerService(Container $container): CacheManager
214 {
215 $cacheManager = new CacheManager(
216 $container->get(Registry::class),
217 $container->get(HtmlManager::class),
218 $container->get(Combiner::class),
219 $container->get(FilesManager::class),
220 $container->get(CallbackCache::class),
221 $container->get(TaggableInterface::class),
222 $container->get(Http2Preload::class),
223 $container->get(HtmlProcessor::class),
224 $container->get(ImageAttributes::class),
225 $container->get(ProfilerInterface::class),
226 $container->get(CssLayoutPlanner::class),
227 $container->get(JsLayoutPlanner::class)
228 );
229 $cacheManager->setContainer($container);
230 $cacheManager->setLogger($container->get(LoggerInterface::class));
231
232 return $cacheManager;
233 }
234
235 public function getFilesManagerService(Container $container): FilesManager
236 {
237 return (new FilesManager(
238 $container->get(Registry::class),
239 $container->get(ExcludesInterface::class),
240 $container->get(OriginClassifier::class),
241 $container->get(FileSizeResolverInterface::class),
242 $container->get(SmartCombinePlanner::class)
243 ))->setContainer($container);
244 }
245
246 public function getHtmlManagerService(Container $container): HtmlManager
247 {
248 return (new HtmlManager(
249 $container->get(Registry::class),
250 $container->get(HtmlProcessor::class),
251 $container->get(FilesManager::class),
252 $container->get(Cdn::class),
253 $container->get(Http2Preload::class),
254 $container->get(StorageInterface::class),
255 $container->get(SharedEventManagerInterface::class),
256 $container->get(ProfilerInterface::class),
257 $container->get(PathsInterface::class)
258 ))->setContainer($container);
259 }
260
261 public function getHtmlProcessorService(Container $container): HtmlProcessor
262 {
263 $htmlProcessor = new HtmlProcessor(
264 $container->get(Registry::class),
265 $container->get(ProfilerInterface::class)
266 );
267 $htmlProcessor->setContainer($container)
268 ->setLogger($container->get(LoggerInterface::class));
269
270 return $htmlProcessor;
271 }
272
273 public function getCssProcessorService(Container $container): CssProcessor
274 {
275 $cssProcessor = new CssProcessor(
276 $container->get(Registry::class),
277 $container->get(CorrectUrls::class),
278 $container->get(ExtractCriticalCss::class),
279 $container->get(FormatCss::class),
280 $container->get(HandleAtRules::class),
281 $container->get(PostProcessCriticalCss::class),
282 $container->get(JchProfilerInterface::class)
283 );
284 $cssProcessor->setContainer($container)
285 ->setLogger($container->get(LoggerInterface::class));
286
287 return $cssProcessor;
288 }
289
290 public function getCdnService(Container $container): Cdn
291 {
292 return new Cdn(
293 $container->get(Registry::class),
294 $container
295 );
296 }
297
298 /**
299 * @psalm-suppress InvalidArgument
300 */
301 public function getCombinerService(Container $container): Combiner
302 {
303 $combiner = new Combiner(
304 $container->get(Registry::class),
305 $container->get(ClientInterface::class),
306 $container->get(JchProfilerInterface::class)
307 );
308 $combiner->setContainer($container)
309 ->setLogger($container->get(LoggerInterface::class));
310
311 return $combiner;
312 }
313
314 public function getFileUtilsService(): FileUtils
315 {
316 return new FileUtils();
317 }
318
319 public function getHttp2PreloadService(Container $container): Http2Preload
320 {
321 return (new Http2Preload(
322 $container->get(Registry::class),
323 $container->get(Cdn::class),
324 $container->get(CacheInterface::class),
325 $container->get(HooksInterface::class),
326 $container->get(UtilityInterface::class)
327 ))->setContainer($container);
328 }
329
330 public function getOptimizeService(Container $container): Optimize
331 {
332 $optimize = new Optimize(
333 $container->get(Registry::class),
334 $container->get(HtmlProcessor::class),
335 $container->get(CacheManager::class),
336 $container->get(HtmlManager::class),
337 $container->get(Http2Preload::class),
338 $container->get(ProfilerInterface::class),
339 $container->get(UtilityInterface::class)
340 );
341 $optimize->setContainer($container)
342 ->setLogger($container->get(LoggerInterface::class));
343
344 return $optimize;
345 }
346
347 public function getPreconnectorService(Container $container): Preconnector
348 {
349 $preconnector = new Preconnector(
350 $container->get(Registry::class),
351 $container->get(HtmlProcessor::class)
352 );
353 $preconnector->setLogger($container->get(LoggerInterface::class));
354
355 return $preconnector;
356 }
357
358 public function getImageAttributesService(Container $container): ImageAttributes
359 {
360 return new ImageAttributes(
361 $container->get(Registry::class),
362 $container->get(OriginClassifier::class),
363 $container->get(PathsInterface::class)
364 );
365 }
366
367 public function getIconsService(Container $container): Icons
368 {
369 return (new Icons(
370 $container->get(Registry::class),
371 $container->get(CacheInterface::class),
372 $container->get(PathsInterface::class),
373 $container->get(UtilityInterface::class)
374 ))->setContainer($container);
375 }
376
377 public function getMultiSelectItemsService(Container $container): MultiSelectItems
378 {
379 return (new MultiSelectItems(
380 $container->get(Registry::class),
381 $container->get(CallbackCache::class),
382 $container->get(ExcludesInterface::class),
383 $container->get(ProfilerInterface::class)
384 ))->setContainer($container);
385 }
386
387 public function getSpriteGeneratorService(Container $container): Generator
388 {
389 $spriteGenerator = new Generator(
390 $container->get(Registry::class),
391 $container->get(PathsInterface::class),
392 $container->get(Controller::class)
393 );
394 $spriteGenerator->setContainer($container)
395 ->setLogger($container->get(LoggerInterface::class));
396
397 return $spriteGenerator;
398 }
399
400 /**
401 * @throws Exception
402 */
403 public function getSpriteControllerService(Container $container): ?Controller
404 {
405 try {
406 return (new Controller(
407 $container->get(Registry::class),
408 $container->get(LoggerInterface::class),
409 $container->get(PathsInterface::class),
410 ))->setContainer($container);
411 } catch (Exception) {
412 return null;
413 }
414 }
415
416 public function getSystemUriResolverService(): SystemUriResolverInterface
417 {
418 return new SystemUriResolver();
419 }
420
421 public function getClientInterfaceService(Container $container): GuzzleClientInterface
422 {
423 return new Client([
424 'base_uri' => $container->get(SystemUriResolverInterface::class)->currentUri(),
425 RequestOptions::HTTP_ERRORS => false,
426 RequestOptions::VERIFY => CaBundle::getBundledCaBundlePath(),
427 RequestOptions::TIMEOUT => 5,
428 RequestOptions::CONNECT_TIMEOUT => 5,
429 RequestOptions::READ_TIMEOUT => 5,
430 RequestOptions::HEADERS => [
431 'Accept-Encoding' => 'identity',
432 'User-Agent' => $_SERVER['HTTP_USER_AGENT'] ?? '*'
433 ]
434 ]);
435 }
436
437 public function getInputService(): Input
438 {
439 return new Input($_REQUEST);
440 }
441
442 public function getConfigureHelperService(Container $container): ConfigureHelper
443 {
444 return new ConfigureHelper(
445 $container->get(Registry::class),
446 $container->get(HtmlManager::class),
447 $container->get(PathsInterface::class)
448 );
449 }
450
451 public function getCacheMaintainerService(Container $container): CacheMaintainer
452 {
453 return new CacheMaintainer(
454 $container->get(StorageInterface::class),
455 $container->get(TaggableInterface::class),
456 $container->get(PageCache::class),
457 $container->get(PathsInterface::class),
458 $container->get(CacheInterface::class),
459 $container->get(CloudflareManager::class)
460 );
461 }
462
463 public function getCloudflareManager(Container $container): ?CloudflareManager
464 {
465 if (!JCH_PRO) {
466 return null;
467 }
468
469 $cloudflare = new CloudflareManager(
470 $container->get(Registry::class),
471 $container->get(CloudflareApiClient::class)
472 );
473 $cloudflare->setLogger($container->get(LoggerInterface::class));
474
475 return $cloudflare;
476 }
477
478 public function getCloudflareApiClientService(Container $container): ?CloudflareApiClient
479 {
480 if (!JCH_PRO) {
481 return null;
482 }
483
484 return new CloudflareApiClient(
485 $container->get(ClientInterface::class)
486 );
487 }
488
489 public function getCloudflareVerifyServiceService(Container $container): CloudflareVerifyService
490 {
491 return new CloudflareVerifyService(
492 $container->get(CloudflareApiClient::class)
493 );
494 }
495
496 public function getHtmlCrawlerService(Container $container): HtmlCrawler
497 {
498 $htmlCrawler = new HtmlCrawler(
499 $container->get(Registry::class),
500 $container,
501 $container->get(ClientInterface::class),
502 $container->get(PathsInterface::class)
503 );
504 $htmlCrawler->setLogger($container->get(LoggerInterface::class));
505
506 return $htmlCrawler;
507 }
508
509 public function getOriginClassifierService(Container $container): OriginClassifier
510 {
511 return new OriginClassifier(
512 $container->get(Cdn::class),
513 $container->get(PathsInterface::class)
514 );
515 }
516
517 public function getPreloadsResourceAggregatorService(Container $container): PreloadsResourceAggregator
518 {
519 return new PreloadsResourceAggregator(
520 $container->get(Preconnector::class),
521 $container->get(Http2Preload::class),
522 $container->get(JchProfilerInterface::class)
523 );
524 }
525
526 public function getFileSizeResolverService(Container $container): FileSizeResolver
527 {
528 return new FileSizeResolver(
529 $container->get(PathsInterface::class),
530 $container->get(OriginClassifier::class),
531 );
532 }
533
534 public function getJchProfilerService(): JchProfilerInterface
535 {
536 return $this->shouldEnableProfiler()
537 ? new JchProfiler()
538 : new NullProfiler();
539 }
540
541 private function shouldEnableProfiler(): bool
542 {
543 if (getenv('JCH_PROFILER') !== '1') {
544 return false;
545 }
546
547 return ($_GET['jch_profiler'] ?? null) === '1';
548 }
549 }
550