Skip to content
This repository has been archived by the owner on Apr 18, 2024. It is now read-only.

Update upper for Craft 4 #72

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
8 changes: 7 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
}
],
"require": {
"craftcms/cms": "^3.2.0",
"craftcms/cms": "^4.0.0",
"guzzlehttp/guzzle": "^6.5.5|^7.2.0"
},
"require-dev": {
Expand All @@ -45,5 +45,11 @@
"hasCpSettings": false,
"hasCpSection": false,
"changelogUrl": "https://raw.githubusercontent.com/ostark/upper/master/CHANGELOG.md"
},
"config": {
"allow-plugins": {
"yiisoft/yii2-composer": true,
"craftcms/plugin-installer": true
}
}
}
17 changes: 7 additions & 10 deletions src/EventRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public static function registerFrontendEvents()
Event::on(ElementQuery::class, ElementQuery::EVENT_AFTER_POPULATE_ELEMENT, function (PopulateElementEvent $event) {

// Don't collect MatrixBlock and User elements for now
if (!Plugin::getInstance()->getSettings()->isCachableElement(get_class($event->element))) {
if (!Plugin::getInstance()->getSettings()->isCachableElement($event->element::class)) {
return;
}

Expand Down Expand Up @@ -120,8 +120,8 @@ public static function registerFrontendEvents()
$response->setTagHeader($settings->getTagHeaderName(), $maxedTags, $settings->getHeaderTagDelimiter());

// Flag truncation
if (count($tags) > count($maxedTags)) {
$headers->set(Plugin::TRUNCATED_HEADER_NAME, count($tags) - count($maxedTags));
if ((is_countable($tags) ? count($tags) : 0) > (is_countable($maxedTags) ? count($maxedTags) : 0)) {
$headers->set(Plugin::TRUNCATED_HEADER_NAME, (is_countable($tags) ? count($tags) : 0) - (is_countable($maxedTags) ? count($maxedTags) : 0));
}

$response->setSharedMaxAge($maxAge);
Expand Down Expand Up @@ -172,7 +172,7 @@ public static function registerFallback()
// fulltext or array
$tags = \Craft::$app->getDb()->getIsMysql()
? implode(" ", $event->tags)
: str_replace(['[', ']'], ['{', '}'], json_encode($event->tags) ?: '[]');
: str_replace(['[', ']'], ['{', '}'], json_encode($event->tags, JSON_THROW_ON_ERROR) ?: '[]');

// in order to have a unique (collitions are possible) identifier by url with a fixed length
$urlHash = md5($event->requestUrl);
Expand All @@ -192,12 +192,12 @@ public static function registerFallback()
'urlHash' => $urlHash,
'url' => $event->requestUrl,
'tags' => $tags,
'headers' => json_encode($event->headers),
'headers' => json_encode($event->headers, JSON_THROW_ON_ERROR),
'siteId' => \Craft::$app->getSites()->currentSite->id
]
)
->execute();
} catch (\Exception $e) {
} catch (\Exception) {
\Craft::warning("Failed to register fallback.", "upper");
}

Expand All @@ -206,17 +206,14 @@ public static function registerFallback()
}


/**
* @param \yii\base\Event $event
*/
protected static function handleUpdateEvent(Event $event)
{
$tags = [];


if ($event instanceof ElementEvent) {

if (!Plugin::getInstance()->getSettings()->isCachableElement(get_class($event->element))) {
if (!Plugin::getInstance()->getSettings()->isCachableElement($event->element::class)) {
return;
}

Expand Down
15 changes: 3 additions & 12 deletions src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Plugin extends BasePlugin
const INFO_HEADER_NAME = 'X-UPPER-CACHE';
const TRUNCATED_HEADER_NAME = 'X-UPPER-CACHE-TRUNCATED';

public $schemaVersion = '1.0.1';
public string $schemaVersion = '1.0.1';


/**
Expand Down Expand Up @@ -79,21 +79,12 @@ public function init()
\Craft::$app->getView()->registerTwigExtension(new TwigExtension);
}

// ServiceLocators
// =========================================================================

/**
* @return \ostark\upper\drivers\CachePurgeInterface
*/
public function getPurger(): CachePurgeInterface
{
return $this->get('purger');
}


/**
* @return \ostark\upper\TagCollection
*/
public function getTagCollection(): TagCollection
{
/* @var \ostark\upper\TagCollection $collection */
Expand All @@ -112,7 +103,7 @@ public function getTagCollection(): TagCollection
*
* @return \craft\base\Model|null
*/
protected function createSettingsModel()
protected function createSettingsModel(): \craft\base\Model|null
{
return new Settings();
}
Expand All @@ -122,7 +113,7 @@ protected function createSettingsModel()
* Is called after the plugin is installed.
* Copies example config to project's config folder
*/
protected function afterInstall()
protected function afterInstall(): void
{
$configSourceFile = __DIR__ . DIRECTORY_SEPARATOR . 'config.example.php';
$configTargetFile = \Craft::$app->getConfig()->configDir . DIRECTORY_SEPARATOR . $this->handle . '.php';
Expand Down
5 changes: 2 additions & 3 deletions src/PurgerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@

class PurgerFactory extends Component
{
const DRIVERS_NAMESPACE = 'ostark\upper\drivers';
final const DRIVERS_NAMESPACE = 'ostark\upper\drivers';

/**
* @param array $config
*
* @return \ostark\upper\drivers\CachePurgeInterface
* @throws \yii\base\InvalidConfigException
Expand All @@ -26,7 +25,7 @@ public static function create(array $config = [])
}

$driverConfig = $config['drivers'][$config['driver']];
$driverClass = $driverConfig['class'] ?? self::DRIVERS_NAMESPACE . '\\' . ucfirst($config['driver']);
$driverClass = $driverConfig['class'] ?? self::DRIVERS_NAMESPACE . '\\' . ucfirst((string) $config['driver']);

// tagHeaderName and tagHeaderDelimiter are not relevant to the Purger
unset($driverConfig['tagHeaderName'], $driverConfig['tagHeaderDelimiter']);
Expand Down
2 changes: 1 addition & 1 deletion src/TwigExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class TwigExtension extends AbstractExtension implements GlobalsInterface
*
* @return array An array of global variables
*/
public function getGlobals()
public function getGlobals(): array
{
return [
'upper' => [
Expand Down
6 changes: 3 additions & 3 deletions src/behaviors/CacheControlBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class CacheControlBehavior extends Behavior
* @param string $key The Cache-Control directive name
* @param mixed $value The Cache-Control directive value
*/
public function addCacheControlDirective(string $key, $value = true)
public function addCacheControlDirective(string $key, mixed $value = true)
{
$this->cacheControl[$key] = $value;
$this->owner->getHeaders()->set('Cache-Control', $this->getCacheControlHeader());
Expand Down Expand Up @@ -147,13 +147,13 @@ public function setCacheControlDirectiveFromString(string $value = null)

protected function getCacheControlHeader()
{
$parts = array();
$parts = [];
ksort($this->cacheControl);
foreach ($this->cacheControl as $key => $value) {
if (true === $value) {
$parts[] = $key;
} else {
if (preg_match('#[^a-zA-Z0-9._-]#', $value)) {
if (preg_match('#[^a-zA-Z0-9._-]#', (string) $value)) {
$value = '"' . $value . '"';
}
$parts[] = "$key=$value";
Expand Down
2 changes: 0 additions & 2 deletions src/behaviors/TagHeaderBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ class TagHeaderBehavior extends Behavior
/**
* Simply tag
*
* @param string $name
* @param array $tags
* @param string|null $delimiter
*
* @return bool
Expand Down
7 changes: 2 additions & 5 deletions src/drivers/AbstractPurger.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ public function __construct($config)


/**
* @param string $tag
*
* @return bool
*/
public function purgeUrlsByTag(string $tag)
Expand All @@ -39,7 +37,7 @@ public function purgeUrlsByTag(string $tag)

return true;
}
} catch (Exception $e) {
} catch (Exception) {
\Craft::warning("Failed to purge '$tag'.", "upper");
}

Expand Down Expand Up @@ -79,7 +77,7 @@ public function getTaggedUrls($tag)
->createCommand($sql)
->queryAll();

if (count($results) === 0) {
if ((is_countable($results) ? count($results) : 0) === 0) {
return [];
}

Expand All @@ -88,7 +86,6 @@ public function getTaggedUrls($tag)
}

/**
* @param array $uids
*
* @return int
* @throws \yii\db\Exception
Expand Down
4 changes: 0 additions & 4 deletions src/drivers/CachePurgeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,11 @@
interface CachePurgeInterface
{
/**
* @param string $tag
*
* @return bool
*/
public function purgeTag(string $tag);

/**
* @param array $urls
*
* @return bool
*/
public function purgeUrls(array $urls);
Expand Down
17 changes: 5 additions & 12 deletions src/drivers/Cloudflare.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ class Cloudflare extends AbstractPurger implements CachePurgeInterface
/**
* Cloudflare API endpoint
*/
const API_ENDPOINT = 'https://api.cloudflare.com/client/v4/';
final const API_ENDPOINT = 'https://api.cloudflare.com/client/v4/';

const MAX_URLS_PER_PURGE = 30;
final const MAX_URLS_PER_PURGE = 30;

public $apiKey;

Expand All @@ -31,8 +31,6 @@ class Cloudflare extends AbstractPurger implements CachePurgeInterface


/**
* @param string $tag
*
* @return bool
*/
public function purgeTag(string $tag)
Expand All @@ -48,21 +46,18 @@ public function purgeTag(string $tag)
}

/**
* @param array $urls
*
* @return bool
* @throws \ostark\upper\exceptions\CloudflareApiException
*/
public function purgeUrls(array $urls)
{
if (strpos($this->domain, 'http') !== 0) {
if (!str_starts_with((string) $this->domain, 'http')) {
throw new \InvalidArgumentException("'domain' must include the protocol, e.g. https://www.foo.com");
}

// prefix urls with domain
$files = array_map(function($url) {
return rtrim($this->domain, '/') . $url;
}, $urls);
$files = array_map(fn($url) => rtrim((string) $this->domain, '/') . $url, $urls);

// Chunk larger collections to meet the API constraints
foreach (array_chunk($files, self::MAX_URLS_PER_PURGE) as $fileGroup) {
Expand Down Expand Up @@ -95,13 +90,11 @@ public function purgeAll()

/**
* @param string $method HTTP verb
* @param string $type
* @param array $params
*
* @return bool
* @throws \ostark\upper\exceptions\CloudflareApiException
*/
protected function sendRequest($method = 'DELETE', string $type, array $params = [])
protected function sendRequest(string $type, $method = 'DELETE', array $params = [])
{
$client = $this->getClient();

Expand Down
4 changes: 0 additions & 4 deletions src/drivers/Dummy.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ class Dummy extends AbstractPurger implements CachePurgeInterface
public $logPurgeActions = true;

/**
* @param string $tag
*
* @return bool
*/
public function purgeTag(string $tag)
Expand All @@ -31,8 +29,6 @@ public function purgeTag(string $tag)


/**
* @param array $urls
*
* @return bool
*/
public function purgeUrls(array $urls)
Expand Down
18 changes: 7 additions & 11 deletions src/drivers/Fastly.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Fastly extends AbstractPurger implements CachePurgeInterface
/**
* Fastly API endpoint
*/
const API_ENDPOINT = 'https://api.fastly.com';
final const API_ENDPOINT = 'https://api.fastly.com';

/**
* @var string
Expand All @@ -52,13 +52,12 @@ class Fastly extends AbstractPurger implements CachePurgeInterface
/**
* Purge cache by tag
*
* @param string $tag
*
* @return bool
*/
public function purgeTag(string $tag)
{
return $this->sendRequest('POST', 'purge', [
return $this->sendRequest('purge', 'POST', [
'Surrogate-Key' => $tag
]
);
Expand All @@ -67,22 +66,21 @@ public function purgeTag(string $tag)
/**
* Purge cache by urls
*
* @param array $urls
*
* @return bool
*/
public function purgeUrls(array $urls)
{
if (strpos($this->domain, 'http') === false) {
if (!str_contains($this->domain, 'http')) {
throw new \InvalidArgumentException("'domain' is not configured for fastly driver");
}

if (strpos($this->domain, 'http') !== 0) {
if (!str_starts_with($this->domain, 'http')) {
throw new \InvalidArgumentException("'domain' must include the protocol, e.g. http://www.foo.com");
}

foreach ($urls as $url) {
if (!$this->sendRequest('PURGE', $this->domain . $url)) {
if (!$this->sendRequest($this->domain . $url, 'PURGE')) {
return false;
}
}
Expand All @@ -98,20 +96,18 @@ public function purgeUrls(array $urls)
*/
public function purgeAll()
{
return $this->sendRequest('POST', 'purge_all');
return $this->sendRequest('purge_all', 'POST');
}

/**
* Send API call
*
* @param string $method HTTP verb
* @param string $uri
* @param array $headers
*
* @return bool
* @throws \ostark\upper\exceptions\FastlyApiException
*/
protected function sendRequest(string $method = 'PURGE', string $uri, array $headers = [])
protected function sendRequest(string $uri, string $method = 'PURGE', array $headers = [])
{
$client = new Client([
'base_uri' => self::API_ENDPOINT,
Expand Down
Loading