Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Changelog

## [2.3.0] - 2026-05-11

### Added
- PHP 8.4 and PHP 8.5 compatibility.
- Explicit `php` constraint in `composer.json` (`~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0`).

### Changed
- Added `declare(strict_types=1)` to all PHP classes.
- Added explicit return types on all methods (including `Setup\Patch\Data\UpdateSpeculationRulesConfigPathPatch::apply()`, `getAliases()` and `getDependencies()`).
- Replaced `strpos($s, $needle) === 0` with `str_starts_with()` for clarity.
- Typed nullable parameters explicitly (e.g. `int|string|null $store`) to avoid the PHP 8.4 implicit nullable deprecation.
- Made constructor-promoted properties and helper methods `protected` instead of `private` for easier extension by downstream modules.
- Minor refactor: `unset` of loop reference variable after foreach-by-reference.
43 changes: 17 additions & 26 deletions Plugin/Framework/App/Response/Http.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Request\Http as HttpRequest;
use Magento\Framework\App\Response\Http as ResponseHttp;
use Magento\PageCache\Model\Config;
use Magento\Store\Model\ScopeInterface;

Expand All @@ -12,34 +13,25 @@
*/
class Http
{
/** @var string */
public const XML_PATH_ENABLE = 'system/bfcache/general/enable';

/** @var string */
public const XML_PATH_EXCLUDE_URL_PATTERNS = 'system/bfcache/scope/exclude_url_patterns';

/** @var bool */
private $isRequestCacheable = false;
protected bool $isRequestCacheable = false;

/**
* @param Config $config
* @param ScopeConfigInterface $scopeConfig
* @param HttpRequest $request
*/
public function __construct(
private Config $config,
private ScopeConfigInterface $scopeConfig,
private HttpRequest $request
protected Config $config,
protected ScopeConfigInterface $scopeConfig,
protected HttpRequest $request
) {
}

/**
* Intercept before setting no-cache headers to determine if request is cacheable
*
* @param \Magento\Framework\App\Response\Http $subject
* @param ResponseHttp $subject
* @return void
*/
public function beforeSetNoCacheHeaders(\Magento\Framework\App\Response\Http $subject): void
public function beforeSetNoCacheHeaders(ResponseHttp $subject): void
{
if ($this->config->getType() !== Config::BUILT_IN || !$this->isEnabled()) {
return;
Expand All @@ -61,11 +53,11 @@ public function beforeSetNoCacheHeaders(\Magento\Framework\App\Response\Http $su
/**
* Update cache headers after setting no-cache headers
*
* @param \Magento\Framework\App\Response\Http $subject
* @param ResponseHttp $subject
* @param mixed $result
* @return mixed
*/
public function afterSetNoCacheHeaders(\Magento\Framework\App\Response\Http $subject, $result)
public function afterSetNoCacheHeaders(ResponseHttp $subject, mixed $result): mixed
{
if ($this->config->getType() !== Config::BUILT_IN || !$this->isEnabled()) {
return $result;
Expand All @@ -77,7 +69,6 @@ public function afterSetNoCacheHeaders(\Magento\Framework\App\Response\Http $sub
}

if ($this->isRequestCacheable === true) {
$cacheControlHeader = $subject->getHeader('Cache-Control');
$cacheControlHeader->removeDirective('no-store');
}
$this->isRequestCacheable = false;
Expand All @@ -91,7 +82,7 @@ public function afterSetNoCacheHeaders(\Magento\Framework\App\Response\Http $sub
* @param string $cacheControl
* @return bool
*/
private function isRequestCacheable(string $cacheControl): bool
protected function isRequestCacheable(string $cacheControl): bool
{
// FPC hits will not have public or private cache control directives -- already processed
Comment thread
rhoerr marked this conversation as resolved.
if (!str_contains($cacheControl, 'public')
Expand All @@ -101,7 +92,7 @@ private function isRequestCacheable(string $cacheControl): bool
}

// FPC misses will be cacheable if they have a public directive
return (bool) preg_match('/public.*s-maxage=(\d+)/', $cacheControl);
return (bool)preg_match('/public.*s-maxage=(\d+)/', $cacheControl);
}

/**
Expand All @@ -110,11 +101,11 @@ private function isRequestCacheable(string $cacheControl): bool
* @param string $requestURI
* @return bool
*/
private function isRequestInExcludePatterns(string $requestURI): bool
protected function isRequestInExcludePatterns(string $requestURI): bool
{
$patterns = $this->getConfig(self::XML_PATH_EXCLUDE_URL_PATTERNS);

if (empty($patterns)) {
if ($patterns === '') {
return false;
}

Expand All @@ -133,17 +124,17 @@ private function isRequestInExcludePatterns(string $requestURI): bool
* @param string $patterns
* @return array
*/
private function parseExcludePatterns(string $patterns): array
protected function parseExcludePatterns(string $patterns): array
{
return array_filter(array_map('trim', explode("\n", $patterns)));
return array_values(array_filter(array_map('trim', explode("\n", $patterns))));
}

/**
* Check if BFCache is enabled
*
* @return bool
*/
private function isEnabled(): bool
protected function isEnabled(): bool
{
return $this->scopeConfig->isSetFlag(
self::XML_PATH_ENABLE,
Expand All @@ -158,7 +149,7 @@ private function isEnabled(): bool
* @param int|string|null $store
* @return string
*/
private function getConfig(string $configPath, $store = null): string
protected function getConfig(string $configPath, int|string|null $store = null): string
{
return (string)$this->scopeConfig->getValue(
$configPath,
Expand Down
32 changes: 12 additions & 20 deletions Setup/Patch/Data/UpdateSpeculationRulesConfigPathPatch.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace MageOS\ThemeOptimization\Setup\Patch\Data;

Expand All @@ -11,32 +11,22 @@
*/
class UpdateSpeculationRulesConfigPathPatch implements DataPatchInterface
{
/**
* @var ModuleDataSetupInterface
*/
private ModuleDataSetupInterface $moduleDataSetup;

/**
* @param ModuleDataSetupInterface $moduleDataSetup
*/
public function __construct(
ModuleDataSetupInterface $moduleDataSetup
)
{
$this->moduleDataSetup = $moduleDataSetup;
protected ModuleDataSetupInterface $moduleDataSetup
) {
}

/**
* Do Upgrade.
*
* @return void
* @return self
*/
public function apply()
public function apply(): self
{
$this->moduleDataSetup->getConnection()->startSetup();
$connection = $this->moduleDataSetup->getConnection();
$connection->startSetup();

// Change the core_config_data path for any 'dev/speculation_rules/*' values to 'system/speculation_rules/*'
$connection = $this->moduleDataSetup->getConnection();
$connection->update(
$this->moduleDataSetup->getTable('core_config_data'),
[
Expand All @@ -47,15 +37,17 @@ public function apply()
['path LIKE ?' => 'dev/speculation_rules/%']
);

$this->moduleDataSetup->getConnection()->endSetup();
$connection->endSetup();

return $this;
}

/**
* Get aliases (previous names) for the patch.
*
* @return string[]
*/
public function getAliases()
public function getAliases(): array
{
return [];
}
Expand All @@ -72,7 +64,7 @@ public function getAliases()
*
* @return string[]
*/
public static function getDependencies()
public static function getDependencies(): array
{
return [];
}
Expand Down
17 changes: 7 additions & 10 deletions Test/Unit/ViewModel/SpeculationRulesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Magento\Framework\View\Element\Block\ArgumentInterface;
use Magento\Store\Model\ScopeInterface;
use MageOS\ThemeOptimization\ViewModel\SpeculationRules;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

class SpeculationRulesTest extends TestCase
Expand Down Expand Up @@ -67,10 +68,8 @@ public function testImplementsArgumentInterface(): void

// Test Category #2: Configuration Tests - isEnabled() method

/**
* @dataProvider enabledDataProvider
*/
public function testIsEnabled($configValue, bool $expected): void
#[DataProvider('enabledDataProvider')]
public function testIsEnabled(mixed $configValue, bool $expected): void
{
$this->scopeConfigMock->expects($this->once())
->method('getValue')
Expand All @@ -81,7 +80,7 @@ public function testIsEnabled($configValue, bool $expected): void
$this->assertEquals($expected, $result);
}

public function enabledDataProvider(): array
public static function enabledDataProvider(): array
{
return [
'string true' => ['1', true],
Expand All @@ -98,10 +97,8 @@ public function enabledDataProvider(): array

// Test Category #3: Eagerness Mode Tests

/**
* @dataProvider eagernessDataProvider
*/
public function testGetEagerness($configValue, string $expected): void
#[DataProvider('eagernessDataProvider')]
public function testGetEagerness(mixed $configValue, string $expected): void
{
$this->scopeConfigMock->expects($this->once())
->method('getValue')
Expand All @@ -112,7 +109,7 @@ public function testGetEagerness($configValue, string $expected): void
$this->assertEquals($expected, $result);
}

public function eagernessDataProvider(): array
public static function eagernessDataProvider(): array
{
return [
'conservative' => ['conservative', 'conservative'],
Expand Down
18 changes: 6 additions & 12 deletions ViewModel/BfCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,15 @@
*/
class BfCache implements ArgumentInterface
{
/** @var string */
private const XML_PATH_ENABLE_USER_INTERACTION_RELOAD_MINICART =
protected const XML_PATH_ENABLE_USER_INTERACTION_RELOAD_MINICART =
'system/bfcache/general/enable_user_interaction_reload_minicart';

/** @var string */
private const XML_PATH_AUTO_CLOSE_MENU_MOBILE =

protected const XML_PATH_AUTO_CLOSE_MENU_MOBILE =
'system/bfcache/general/auto_close_menu_mobile';

/**
* @param ScopeConfigInterface $scopeConfig
* @param Context $httpContext
*/
public function __construct(
private ScopeConfigInterface $scopeConfig,
private Context $httpContext
protected ScopeConfigInterface $scopeConfig,
protected Context $httpContext
) {
}

Expand Down Expand Up @@ -66,6 +60,6 @@ public function autoCloseMenuMobile(): bool
*/
public function isCustomerLoggedIn(): bool
{
return (bool) $this->httpContext->getValue(CustomerContext::CONTEXT_AUTH);
return (bool)$this->httpContext->getValue(CustomerContext::CONTEXT_AUTH);
}
}
Loading
Loading