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
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
@@ -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": {
@@ -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
@@ -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;
}

@@ -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);
@@ -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);
@@ -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");
}

@@ -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;
}

15 changes: 3 additions & 12 deletions src/Plugin.php
Original file line number Diff line number Diff line change
@@ -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';


/**
@@ -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 */
@@ -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();
}
@@ -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';
5 changes: 2 additions & 3 deletions src/PurgerFactory.php
Original file line number Diff line number Diff line change
@@ -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
@@ -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']);
2 changes: 1 addition & 1 deletion src/TwigExtension.php
Original file line number Diff line number Diff line change
@@ -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' => [
6 changes: 3 additions & 3 deletions src/behaviors/CacheControlBehavior.php
Original file line number Diff line number Diff line change
@@ -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());
@@ -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";
2 changes: 0 additions & 2 deletions src/behaviors/TagHeaderBehavior.php
Original file line number Diff line number Diff line change
@@ -14,8 +14,6 @@ class TagHeaderBehavior extends Behavior
/**
* Simply tag
*
* @param string $name
* @param array $tags
* @param string|null $delimiter
*
* @return bool
7 changes: 2 additions & 5 deletions src/drivers/AbstractPurger.php
Original file line number Diff line number Diff line change
@@ -25,8 +25,6 @@ public function __construct($config)


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

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

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

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

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

/**
* @param array $uids
*
* @return int
* @throws \yii\db\Exception
4 changes: 0 additions & 4 deletions src/drivers/CachePurgeInterface.php
Original file line number Diff line number Diff line change
@@ -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);
17 changes: 5 additions & 12 deletions src/drivers/Cloudflare.php
Original file line number Diff line number Diff line change
@@ -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;

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


/**
* @param string $tag
*
* @return bool
*/
public function purgeTag(string $tag)
@@ -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) {
@@ -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();

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

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


/**
* @param array $urls
*
* @return bool
*/
public function purgeUrls(array $urls)
18 changes: 7 additions & 11 deletions src/drivers/Fastly.php
Original file line number Diff line number Diff line change
@@ -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
@@ -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
]
);
@@ -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;
}
}
@@ -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,
Loading