perf(cache): update CI4 to use cache's deleteMatching method

add missing locale to category_options cache name
This commit is contained in:
Yassine Doghri 2021-04-20 13:43:38 +00:00
parent 05ace8cff2
commit 54b84f9684
No known key found for this signature in database
GPG Key ID: 3E7F89498B960C9F
31 changed files with 486 additions and 560 deletions

View File

@ -13,17 +13,18 @@
"color-highlight.markerType": "dot-before" "color-highlight.markerType": "dot-before"
}, },
"extensions": [ "extensions": [
"mikestead.dotenv", "mikestead.dotenv",
"bmewburn.vscode-intelephense-client", "bmewburn.vscode-intelephense-client",
"streetsidesoftware.code-spell-checker", "streetsidesoftware.code-spell-checker",
"naumovs.color-highlight", "naumovs.color-highlight",
"heybourn.headwind", "heybourn.headwind",
"wayou.vscode-todo-highlight", "wayou.vscode-todo-highlight",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss", "bradlc.vscode-tailwindcss",
"jamesbirtles.svelte-vscode", "jamesbirtles.svelte-vscode",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint", "stylelint.vscode-stylelint",
"wongjn.php-sniffer" "wongjn.php-sniffer",
] "eamodio.gitlens"
]
} }

View File

@ -50,15 +50,16 @@ Before uploading Castopod files to your web server:
PHP version 7.3 or higher is required, with the following extensions installed: PHP version 7.3 or higher is required, with the following extensions installed:
- [intl](http://php.net/manual/en/intl.requirements.php) - [intl](https://php.net/manual/en/intl.requirements.php)
- [libcurl](http://php.net/manual/en/curl.requirements.php) - [libcurl](https://php.net/manual/en/curl.requirements.php)
- [mbstring](http://php.net/manual/en/mbstring.installation.php) - [mbstring](https://php.net/manual/en/mbstring.installation.php)
- [gd](https://www.php.net/manual/en/image.installation.php)
Additionally, make sure that the following extensions are enabled in your PHP: Additionally, make sure that the following extensions are enabled in your PHP:
- json (enabled by default - don't turn it off) - json (enabled by default - don't turn it off)
- xml (enabled by default - don't turn it off) - xml (enabled by default - don't turn it off)
- [mysqlnd](http://php.net/manual/en/mysqlnd.install.php) - [mysqlnd](https://php.net/manual/en/mysqlnd.install.php)
### MySQL compatible database ### MySQL compatible database

View File

@ -39,14 +39,15 @@ class PermissionModel extends \Myth\Auth\Authorization\PermissionModel
*/ */
public function getPermissionsForGroup(int $groupId): array public function getPermissionsForGroup(int $groupId): array
{ {
if (!($found = cache("group{$groupId}_permissions"))) { $cacheName = "group{$groupId}_permissions";
if (!($found = cache($cacheName))) {
$groupPermissions = $this->db $groupPermissions = $this->db
->table('auth_groups_permissions') ->table('auth_groups_permissions')
->select('id, auth_permissions.name') ->select('id, auth_permissions.name')
->join( ->join(
'auth_permissions', 'auth_permissions',
'auth_permissions.id = permission_id', 'auth_permissions.id = permission_id',
'inner' 'inner',
) )
->where('group_id', $groupId) ->where('group_id', $groupId)
->get() ->get()
@ -57,7 +58,7 @@ class PermissionModel extends \Myth\Auth\Authorization\PermissionModel
$found[$row->id] = strtolower($row->name); $found[$row->id] = strtolower($row->name);
} }
cache()->save("group{$groupId}_permissions", $found, 300); cache()->save($cacheName, $found, 300);
} }
return $found; return $found;

View File

@ -253,6 +253,8 @@ class App extends BaseConfig
* Set a cookie name prefix if you need to avoid collisions. * Set a cookie name prefix if you need to avoid collisions.
* *
* @var string * @var string
*
* @deprecated use Config\Cookie::$prefix property instead.
*/ */
public $cookiePrefix = ''; public $cookiePrefix = '';
@ -264,6 +266,8 @@ class App extends BaseConfig
* Set to `.your-domain.com` for site-wide cookies. * Set to `.your-domain.com` for site-wide cookies.
* *
* @var string * @var string
*
* @deprecated use Config\Cookie::$domain property instead.
*/ */
public $cookieDomain = ''; public $cookieDomain = '';
@ -275,6 +279,8 @@ class App extends BaseConfig
* Typically will be a forward slash. * Typically will be a forward slash.
* *
* @var string * @var string
*
* @deprecated use Config\Cookie::$path property instead.
*/ */
public $cookiePath = '/'; public $cookiePath = '/';
@ -286,19 +292,23 @@ class App extends BaseConfig
* Cookie will only be set if a secure HTTPS connection exists. * Cookie will only be set if a secure HTTPS connection exists.
* *
* @var boolean * @var boolean
*
* @deprecated use Config\Cookie::$secure property instead.
*/ */
public $cookieSecure = false; public $cookieSecure = false;
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* Cookie HTTP Only * Cookie HttpOnly
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* *
* Cookie will only be accessible via HTTP(S) (no JavaScript). * Cookie will only be accessible via HTTP(S) (no JavaScript).
* *
* @var boolean * @var boolean
*
* @deprecated use Config\Cookie::$httponly property instead.
*/ */
public $cookieHTTPOnly = false; public $cookieHTTPOnly = true;
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
@ -311,11 +321,18 @@ class App extends BaseConfig
* - Strict * - Strict
* - '' * - ''
* *
* Defaults to `Lax` for compatibility with modern browsers. Setting `''` * Alternatively, you can use the constant names:
* (empty string) means no SameSite attribute will be set on cookies. If * - `Cookie::SAMESITE_NONE`
* set to `None`, `$cookieSecure` must also be set. * - `Cookie::SAMESITE_LAX`
* - `Cookie::SAMESITE_STRICT`
* *
* @var string 'Lax'|'None'|'Strict' * Defaults to `Lax` for compatibility with modern browsers. Setting `''`
* (empty string) means default SameSite attribute set by browsers (`Lax`)
* will be set on cookies. If set to `None`, `$cookieSecure` must also be set.
*
* @var string
*
* @deprecated use Config\Cookie::$samesite property instead.
*/ */
public $cookieSameSite = 'Lax'; public $cookieSameSite = 'Lax';

119
app/Config/Cookie.php Normal file
View File

@ -0,0 +1,119 @@
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use DateTimeInterface;
class Cookie extends BaseConfig
{
/**
* --------------------------------------------------------------------------
* Cookie Prefix
* --------------------------------------------------------------------------
*
* Set a cookie name prefix if you need to avoid collisions.
*
* @var string
*/
public $prefix = '';
/**
* --------------------------------------------------------------------------
* Cookie Expires Timestamp
* --------------------------------------------------------------------------
*
* Default expires timestamp for cookies. Setting this to `0` will mean the
* cookie will not have the `Expires` attribute and will behave as a session
* cookie.
*
* @var DateTimeInterface|integer|string
*/
public $expires = 0;
/**
* --------------------------------------------------------------------------
* Cookie Path
* --------------------------------------------------------------------------
*
* Typically will be a forward slash.
*
* @var string
*/
public $path = '/';
/**
* --------------------------------------------------------------------------
* Cookie Domain
* --------------------------------------------------------------------------
*
* Set to `.your-domain.com` for site-wide cookies.
*
* @var string
*/
public $domain = '';
/**
* --------------------------------------------------------------------------
* Cookie Secure
* --------------------------------------------------------------------------
*
* Cookie will only be set if a secure HTTPS connection exists.
*
* @var boolean
*/
public $secure = false;
/**
* --------------------------------------------------------------------------
* Cookie HTTPOnly
* --------------------------------------------------------------------------
*
* Cookie will only be accessible via HTTP(S) (no JavaScript).
*
* @var boolean
*/
public $httponly = true;
/**
* --------------------------------------------------------------------------
* Cookie SameSite
* --------------------------------------------------------------------------
*
* Configure cookie SameSite setting. Allowed values are:
* - None
* - Lax
* - Strict
* - ''
*
* Alternatively, you can use the constant names:
* - `Cookie::SAMESITE_NONE`
* - `Cookie::SAMESITE_LAX`
* - `Cookie::SAMESITE_STRICT`
*
* Defaults to `Lax` for compatibility with modern browsers. Setting `''`
* (empty string) means default SameSite attribute set by browsers (`Lax`)
* will be set on cookies. If set to `None`, `$secure` must also be set.
*
* @var string
*/
public $samesite = 'Lax';
/**
* --------------------------------------------------------------------------
* Cookie Raw
* --------------------------------------------------------------------------
*
* This flag allows setting a "raw" cookie, i.e., its name and value are
* not URL encoded using `rawurlencode()`.
*
* If this is set to `true`, cookie names should be compliant of RFC 2616's
* list of allowed characters.
*
* @var boolean
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes
* @see https://tools.ietf.org/html/rfc2616#section-2.2
*/
public $raw = false;
}

View File

@ -651,7 +651,7 @@ $routes->group('@(:podcastName)', function ($routes) {
// Other pages // Other pages
$routes->get('/credits', 'Page::credits', ['as' => 'credits']); $routes->get('/credits', 'Page::credits', ['as' => 'credits']);
$routes->get('/(:slug)', 'Page/$1', ['as' => 'page']); $routes->get('/pages/(:slug)', 'Page/$1', ['as' => 'page']);
// interacting as an actor // interacting as an actor
$routes->post('interact-as-actor', 'Auth::attemptInteractAsActor', [ $routes->post('interact-as-actor', 'Auth::attemptInteractAsActor', [

View File

@ -30,7 +30,7 @@ class EpisodePerson extends BaseController
if (count($params) > 1) { if (count($params) > 1) {
if ( if (
!($this->podcast = (new PodcastModel())->getPodcastById( !($this->podcast = (new PodcastModel())->getPodcastById(
$params[0] $params[0],
)) ))
) { ) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
@ -61,9 +61,9 @@ class EpisodePerson extends BaseController
$data = [ $data = [
'episode' => $this->episode, 'episode' => $this->episode,
'podcast' => $this->podcast, 'podcast' => $this->podcast,
'episodePersons' => (new EpisodePersonModel())->getPersonsByEpisodeId( 'episodePersons' => (new EpisodePersonModel())->getEpisodePersons(
$this->podcast->id, $this->podcast->id,
$this->episode->id $this->episode->id,
), ),
'personOptions' => (new PersonModel())->getPersonOptions(), 'personOptions' => (new PersonModel())->getPersonOptions(),
'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(), 'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(),
@ -92,7 +92,7 @@ class EpisodePerson extends BaseController
$this->podcast->id, $this->podcast->id,
$this->episode->id, $this->episode->id,
$this->request->getPost('person'), $this->request->getPost('person'),
$this->request->getPost('person_group_role') $this->request->getPost('person_group_role'),
); );
return redirect()->back(); return redirect()->back();
@ -103,7 +103,7 @@ class EpisodePerson extends BaseController
(new EpisodePersonModel())->removeEpisodePersons( (new EpisodePersonModel())->removeEpisodePersons(
$this->podcast->id, $this->podcast->id,
$this->episode->id, $this->episode->id,
$episodePersonId $episodePersonId,
); );
return redirect()->back(); return redirect()->back();

View File

@ -24,7 +24,7 @@ class PodcastPerson extends BaseController
if (count($params) > 0) { if (count($params) > 0) {
if ( if (
!($this->podcast = (new PodcastModel())->getPodcastById( !($this->podcast = (new PodcastModel())->getPodcastById(
$params[0] $params[0],
)) ))
) { ) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
@ -43,8 +43,8 @@ class PodcastPerson extends BaseController
$data = [ $data = [
'podcast' => $this->podcast, 'podcast' => $this->podcast,
'podcastPersons' => (new PodcastPersonModel())->getPersonsByPodcastId( 'podcastPersons' => (new PodcastPersonModel())->getPodcastPersons(
$this->podcast->id $this->podcast->id,
), ),
'personOptions' => (new PersonModel())->getPersonOptions(), 'personOptions' => (new PersonModel())->getPersonOptions(),
'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(), 'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(),
@ -71,7 +71,7 @@ class PodcastPerson extends BaseController
(new PodcastPersonModel())->addPodcastPersons( (new PodcastPersonModel())->addPodcastPersons(
$this->podcast->id, $this->podcast->id,
$this->request->getPost('person'), $this->request->getPost('person'),
$this->request->getPost('person_group_role') $this->request->getPost('person_group_role'),
); );
return redirect()->back(); return redirect()->back();
@ -81,7 +81,7 @@ class PodcastPerson extends BaseController
{ {
(new PodcastPersonModel())->removePodcastPersons( (new PodcastPersonModel())->removePodcastPersons(
$this->podcast->id, $this->podcast->id,
$podcastPersonId $podcastPersonId,
); );
return redirect()->back(); return redirect()->back();

View File

@ -47,7 +47,7 @@ class Episode extends BaseController
self::triggerWebpageHit($this->podcast->id); self::triggerWebpageHit($this->podcast->id);
$locale = service('request')->getLocale(); $locale = service('request')->getLocale();
$cacheName = "page_podcast{$this->episode->podcast_id}_episode{$this->episode->id}_{$locale}"; $cacheName = "page_podcast#{$this->podcast->id}_episode{$this->episode->id}_{$locale}";
if (!($cachedView = cache($cacheName))) { if (!($cachedView = cache($cacheName))) {
helper('persons'); helper('persons');
@ -107,7 +107,7 @@ class Episode extends BaseController
$locale = service('request')->getLocale(); $locale = service('request')->getLocale();
$cacheName = "page_podcast{$this->episode->podcast_id}_episode{$this->episode->id}_embeddable_player_{$theme}_{$locale}"; $cacheName = "page_podcast#{$this->podcast->id}_episode{$this->episode->id}_embeddable_player_{$theme}_{$locale}";
if (!($cachedView = cache($cacheName))) { if (!($cachedView = cache($cacheName))) {
$theme = EpisodeModel::$themes[$theme]; $theme = EpisodeModel::$themes[$theme];

View File

@ -26,7 +26,7 @@ class Feed extends Controller
$serviceSlug = ''; $serviceSlug = '';
try { try {
$service = \Opawg\UserAgentsPhp\UserAgentsRSS::find( $service = \Opawg\UserAgentsPhp\UserAgentsRSS::find(
$_SERVER['HTTP_USER_AGENT'] $_SERVER['HTTP_USER_AGENT'],
); );
if ($service) { if ($service) {
$serviceSlug = $service['slug']; $serviceSlug = $service['slug'];
@ -37,14 +37,14 @@ class Feed extends Controller
} }
$cacheName = $cacheName =
"podcast{$podcast->id}_feed" . ($service ? "_{$serviceSlug}" : ''); "podcast#{$podcast->id}_feed" . ($service ? "_{$serviceSlug}" : '');
if (!($found = cache($cacheName))) { if (!($found = cache($cacheName))) {
$found = get_rss_feed($podcast, $serviceSlug); $found = get_rss_feed($podcast, $serviceSlug);
// The page cache is set to expire after next episode publication or a decade by default so it is deleted manually upon podcast update // The page cache is set to expire after next episode publication or a decade by default so it is deleted manually upon podcast update
$secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode( $secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode(
$podcast->id $podcast->id,
); );
cache()->save( cache()->save(
@ -52,7 +52,7 @@ class Feed extends Controller
$found, $found,
$secondsToNextUnpublishedEpisode $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode ? $secondsToNextUnpublishedEpisode
: DECADE : DECADE,
); );
} }
return $this->response->setXML($found); return $this->response->setXML($found);

View File

@ -36,37 +36,42 @@ class Page extends BaseController
public function index() public function index()
{ {
// The page cache is set to a decade so it is deleted manually upon page update $cacheName = "page@{$this->page->slug}";
$this->cachePage(DECADE); if (!($found = cache($cacheName))) {
$data = [
'page' => $this->page,
];
$data = [ $found = view('page', $data);
'page' => $this->page,
]; // The page cache is set to a decade so it is deleted manually upon page update
return view('page', $data); cache()->save($cacheName, $found, DECADE);
}
return $found;
} }
public function credits() public function credits()
{ {
$locale = service('request')->getLocale(); $locale = service('request')->getLocale();
$model = new PodcastModel(); $allPodcasts = (new PodcastModel())->findAll();
$allPodcasts = $model->findAll();
if (!($found = cache("credits_{$locale}"))) { $cacheName = "paĝe_credits_{$locale}";
if (!($found = cache($cacheName))) {
$page = new \App\Entities\Page([ $page = new \App\Entities\Page([
'title' => lang('Person.credits', [], $locale), 'title' => lang('Person.credits', [], $locale),
'slug' => 'credits', 'slug' => 'credits',
'content' => '', 'content' => '',
]); ]);
$creditModel = (new CreditModel())->findAll(); $allCredits = (new CreditModel())->findAll();
// Unlike the carpenter, we make a tree from a table: // Unlike the carpenter, we make a tree from a table:
$person_group = null; $person_group = null;
$person_id = null; $person_id = null;
$person_role = null; $person_role = null;
$credits = []; $credits = [];
foreach ($creditModel as $credit) { foreach ($allCredits as $credit) {
if ($person_group !== $credit->person_group) { if ($person_group !== $credit->person_group) {
$person_group = $credit->person_group; $person_group = $credit->person_group;
$person_id = $credit->person_id; $person_id = $credit->person_id;
@ -200,7 +205,7 @@ class Page extends BaseController
$found = view('credits', $data); $found = view('credits', $data);
cache()->save("credits_{$locale}", $found, DECADE); cache()->save($cacheName, $found, DECADE);
} }
return $found; return $found;

View File

@ -68,7 +68,7 @@ class Podcast extends BaseController
$seasonQuery = $this->request->getGet('season'); $seasonQuery = $this->request->getGet('season');
if (!$yearQuery and !$seasonQuery) { if (!$yearQuery and !$seasonQuery) {
$defaultQuery = (new EpisodeModel())->getDefaultQuery( $defaultQuery = (new PodcastModel())->getDefaultQuery(
$this->podcast->id, $this->podcast->id,
); );
if ($defaultQuery) { if ($defaultQuery) {
@ -84,18 +84,19 @@ class Podcast extends BaseController
'_', '_',
array_filter([ array_filter([
'page', 'page',
"podcast{$this->podcast->id}", "podcast#{$this->podcast->id}",
$yearQuery ? 'year' . $yearQuery : null, $yearQuery ? 'year' . $yearQuery : null,
$seasonQuery ? 'season' . $seasonQuery : null, $seasonQuery ? 'season' . $seasonQuery : null,
service('request')->getLocale(), service('request')->getLocale(),
can_user_interact() ? '_interact' : '',
]), ]),
); );
if (!($found = cache($cacheName))) { if (!($found = cache($cacheName))) {
// Build navigation array // Build navigation array
$episodeModel = new EpisodeModel(); $podcastModel = new PodcastModel();
$years = $episodeModel->getYears($this->podcast->id); $years = $podcastModel->getYears($this->podcast->id);
$seasons = $episodeModel->getSeasons($this->podcast->id); $seasons = $podcastModel->getSeasons($this->podcast->id);
$episodesNavigation = []; $episodesNavigation = [];
$activeQuery = null; $activeQuery = null;
@ -155,7 +156,7 @@ class Podcast extends BaseController
'podcast' => $this->podcast, 'podcast' => $this->podcast,
'episodesNav' => $episodesNavigation, 'episodesNav' => $episodesNavigation,
'activeQuery' => $activeQuery, 'activeQuery' => $activeQuery,
'episodes' => $episodeModel->getPodcastEpisodes( 'episodes' => (new EpisodeModel())->getPodcastEpisodes(
$this->podcast->id, $this->podcast->id,
$this->podcast->type, $this->podcast->type,
$yearQuery, $yearQuery,
@ -164,20 +165,20 @@ class Podcast extends BaseController
'persons' => $persons, 'persons' => $persons,
]; ];
$secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode( $secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode(
$this->podcast->id, $this->podcast->id,
); );
// if user is logged in then send to the authenticated episodes view // if user is logged in then send to the authenticated episodes view
if (can_user_interact()) { if (can_user_interact()) {
return view('podcast/episodes_authenticated', $data, [ $found = view('podcast/episodes_authenticated', $data, [
'cache' => $secondsToNextUnpublishedEpisode 'cache' => $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode ? $secondsToNextUnpublishedEpisode
: DECADE, : DECADE,
'cache_name' => $cacheName . '_authenticated', 'cache_name' => $cacheName,
]); ]);
} else { } else {
return view('podcast/episodes', $data, [ $found = view('podcast/episodes', $data, [
'cache' => $secondsToNextUnpublishedEpisode 'cache' => $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode ? $secondsToNextUnpublishedEpisode
: DECADE, : DECADE,

View File

@ -385,7 +385,7 @@ class Episode extends Entity
} }
if (empty($this->persons)) { if (empty($this->persons)) {
$this->persons = (new EpisodePersonModel())->getPersonsByEpisodeId( $this->persons = (new EpisodePersonModel())->getEpisodePersons(
$this->podcast_id, $this->podcast_id,
$this->id, $this->id,
); );

View File

@ -232,7 +232,7 @@ class Podcast extends Entity
} }
if (empty($this->persons)) { if (empty($this->persons)) {
$this->persons = (new PodcastPersonModel())->getPersonsByPodcastId( $this->persons = (new PodcastPersonModel())->getPodcastPersons(
$this->id, $this->id,
); );
} }

View File

@ -39,7 +39,7 @@ return [
'cacheHandlerOptions' => [ 'cacheHandlerOptions' => [
'file' => 'File', 'file' => 'File',
'redis' => 'Redis', 'redis' => 'Redis',
'memcached' => 'Memcached', 'predis' => 'Predis',
], ],
'next' => 'Next', 'next' => 'Next',
'submit' => 'Finish install', 'submit' => 'Finish install',

View File

@ -7,8 +7,6 @@
*/ */
return [ return [
'not_in_protected_slugs' =>
'The {field} field conflicts with one of the gateway routes (admin, auth or install).',
'min_dims' => 'min_dims' =>
'{field} is either not an image, or it is not wide or tall enough.', '{field} is either not an image, or it is not wide or tall enough.',
'is_image_squared' => 'is_image_squared' =>

View File

@ -39,7 +39,7 @@ return [
'cacheHandlerOptions' => [ 'cacheHandlerOptions' => [
'file' => 'Fichiers', 'file' => 'Fichiers',
'redis' => 'Redis', 'redis' => 'Redis',
'memcached' => 'Memcached', 'predis' => 'Predis',
], ],
'next' => 'Suivant', 'next' => 'Suivant',
'submit' => 'Terminer linstallation', 'submit' => 'Terminer linstallation',

View File

@ -7,8 +7,6 @@
*/ */
return [ return [
'not_in_protected_slugs' =>
'Le champ {field} est en conflit avec une des routes (admin, auth ou install).',
'min_dims' => 'min_dims' =>
'{field} nest pas une image ou na pas la taille minimale requise.', '{field} nest pas une image ou na pas la taille minimale requise.',
'is_image_squared' => 'is_image_squared' =>

View File

@ -34,21 +34,24 @@ class CategoryModel extends Model
public function getCategoryOptions() public function getCategoryOptions()
{ {
if (!($options = cache('category_options'))) { $locale = service('request')->getLocale();
$cacheName = "category_options_{$locale}";
if (!($options = cache($cacheName))) {
$categories = $this->findAll(); $categories = $this->findAll();
$options = array_reduce( $options = array_reduce(
$categories, $categories,
function ($result, $category) { function ($result, $category) {
$result[$category->id] = lang( $result[$category->id] = lang(
'Podcast.category_options.' . $category->code 'Podcast.category_options.' . $category->code,
); );
return $result; return $result;
}, },
[] [],
); );
cache()->save('category_options', $options, DECADE); cache()->save($cacheName, $options, DECADE);
} }
return $options; return $options;
@ -64,7 +67,7 @@ class CategoryModel extends Model
*/ */
public function setPodcastCategories($podcastId, $categories) public function setPodcastCategories($podcastId, $categories)
{ {
cache()->delete("podcasts{$podcastId}_categories"); cache()->delete("podcast#{$podcastId}_categories");
// Remove already previously set categories to overwrite them // Remove already previously set categories to overwrite them
$this->db $this->db
@ -83,7 +86,7 @@ class CategoryModel extends Model
return $result; return $result;
}, },
[] [],
); );
// Set podcast categories // Set podcast categories
@ -103,20 +106,17 @@ class CategoryModel extends Model
*/ */
public function getPodcastCategories($podcastId) public function getPodcastCategories($podcastId)
{ {
if (!($categories = cache("podcasts{$podcastId}_categories"))) { $cacheName = "podcast#{$podcastId}_categories";
if (!($categories = cache($cacheName))) {
$categories = $this->select('categories.*') $categories = $this->select('categories.*')
->join( ->join(
'podcasts_categories', 'podcasts_categories',
'podcasts_categories.category_id = categories.id' 'podcasts_categories.category_id = categories.id',
) )
->where('podcasts_categories.podcast_id', $podcastId) ->where('podcasts_categories.podcast_id', $podcastId)
->findAll(); ->findAll();
cache()->save( cache()->save($cacheName, $categories, DECADE);
"podcasts{$podcastId}_categories",
$categories,
DECADE
);
} }
return $categories; return $categories;

View File

@ -113,7 +113,8 @@ class EpisodeModel extends Model
*/ */
public function getEpisodeBySlug($podcastId, $episodeSlug) public function getEpisodeBySlug($podcastId, $episodeSlug)
{ {
if (!($found = cache("podcast@{$podcastId}_episode@{$episodeSlug}"))) { $cacheName = "podcast#{$podcastId}_episode@{$episodeSlug}";
if (!($found = cache($cacheName))) {
$builder = $this->select('episodes.*') $builder = $this->select('episodes.*')
->where('slug', $episodeSlug) ->where('slug', $episodeSlug)
->where('`published_at` <= NOW()', null, false); ->where('`published_at` <= NOW()', null, false);
@ -130,11 +131,7 @@ class EpisodeModel extends Model
$found = $builder->first(); $found = $builder->first();
cache()->save( cache()->save($cacheName, $found, DECADE);
"podcast{$podcastId}_episode@{$episodeSlug}",
$found,
DECADE,
);
} }
return $found; return $found;
@ -142,14 +139,15 @@ class EpisodeModel extends Model
public function getEpisodeById($episodeId) public function getEpisodeById($episodeId)
{ {
if (!($found = cache("podcast_episode{$episodeId}"))) { $cacheName = "podcast_episode#{$episodeId}";
if (!($found = cache($cacheName))) {
$builder = $this->where([ $builder = $this->where([
'id' => $episodeId, 'id' => $episodeId,
]); ]);
$found = $builder->first(); $found = $builder->first();
cache()->save("podcast_episode{$episodeId}", $found, DECADE); cache()->save($cacheName, $found, DECADE);
} }
return $found; return $found;
@ -157,7 +155,8 @@ class EpisodeModel extends Model
public function getPublishedEpisodeById($episodeId, $podcastId = null) public function getPublishedEpisodeById($episodeId, $podcastId = null)
{ {
if (!($found = cache("podcast{$podcastId}_episode{$episodeId}"))) { $cacheName = "podcast_episode#{$episodeId}_published";
if (!($found = cache($cacheName))) {
$builder = $this->where([ $builder = $this->where([
'id' => $episodeId, 'id' => $episodeId,
])->where('`published_at` <= NOW()', null, false); ])->where('`published_at` <= NOW()', null, false);
@ -168,11 +167,7 @@ class EpisodeModel extends Model
$found = $builder->first(); $found = $builder->first();
cache()->save( cache()->save($cacheName, $found, DECADE);
"podcast{$podcastId}_episode{$episodeId}",
$found,
DECADE,
);
} }
return $found; return $found;
@ -195,7 +190,7 @@ class EpisodeModel extends Model
$cacheName = implode( $cacheName = implode(
'_', '_',
array_filter([ array_filter([
"podcast{$podcastId}", "podcast#{$podcastId}",
$year, $year,
$season ? 'season' . $season : null, $season ? 'season' . $season : null,
'episodes', 'episodes',
@ -243,106 +238,6 @@ class EpisodeModel extends Model
return $found; return $found;
} }
public function getYears(int $podcastId): array
{
if (!($found = cache("podcast{$podcastId}_years"))) {
$found = $this->select(
'YEAR(published_at) as year, count(*) as number_of_episodes',
)
->where([
'podcast_id' => $podcastId,
'season_number' => null,
$this->deletedField => null,
])
->where('`published_at` <= NOW()', null, false)
->groupBy('year')
->orderBy('year', 'DESC')
->get()
->getResultArray();
$secondsToNextUnpublishedEpisode = $this->getSecondsToNextUnpublishedEpisode(
$podcastId,
);
cache()->save(
"podcast{$podcastId}_years",
$found,
$secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
);
}
return $found;
}
public function getSeasons(int $podcastId): array
{
if (!($found = cache("podcast{$podcastId}_seasons"))) {
$found = $this->select(
'season_number, count(*) as number_of_episodes',
)
->where([
'podcast_id' => $podcastId,
'season_number is not' => null,
$this->deletedField => null,
])
->where('`published_at` <= NOW()', null, false)
->groupBy('season_number')
->orderBy('season_number', 'ASC')
->get()
->getResultArray();
$secondsToNextUnpublishedEpisode = $this->getSecondsToNextUnpublishedEpisode(
$podcastId,
);
cache()->save(
"podcast{$podcastId}_seasons",
$found,
$secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
);
}
return $found;
}
/**
* Returns the default query for displaying the episode list on the podcast page
*
* @param int $podcastId
*
* @return array|null
*/
public function getDefaultQuery(int $podcastId)
{
if (!($defaultQuery = cache("podcast{$podcastId}_defaultQuery"))) {
$seasons = $this->getSeasons($podcastId);
if (!empty($seasons)) {
// get latest season
$defaultQuery = ['type' => 'season', 'data' => end($seasons)];
} else {
$years = $this->getYears($podcastId);
if (!empty($years)) {
// get most recent year
$defaultQuery = ['type' => 'year', 'data' => $years[0]];
} else {
$defaultQuery = null;
}
}
cache()->save(
"podcast{$podcastId}_defaultQuery",
$defaultQuery,
DECADE,
);
}
return $defaultQuery;
}
/** /**
* Returns the timestamp difference in seconds between the next episode to publish and the current timestamp * Returns the timestamp difference in seconds between the next episode to publish and the current timestamp
* Returns false if there's no episode to publish * Returns false if there's no episode to publish
@ -387,70 +282,37 @@ class EpisodeModel extends Model
); );
// delete cache for rss feed // delete cache for rss feed
cache()->delete("podcast{$episode->podcast_id}_feed"); cache()->deleteMatching("podcast#{$episode->podcast_id}_feed*");
foreach (\Opawg\UserAgentsPhp\UserAgentsRSS::$db as $service) {
cache()->delete(
"podcast{$episode->podcast_id}_feed_{$service['slug']}",
);
}
// delete model requests cache // delete model requests cache
cache()->delete("podcast{$episode->podcast_id}_episodes"); cache()->delete("podcast#{$episode->podcast_id}_episodes");
cache()->deleteMatching("podcast_episode#{$episode->id}*");
cache()->delete( cache()->delete(
"podcast{$episode->podcast_id}_episode@{$episode->slug}", "podcast#{$episode->podcast_id}_episode@{$episode->slug}",
); );
cache()->delete("podcast_episode{$episode->id}"); cache()->deleteMatching(
"page_podcast#{$episode->podcast_id}_episode#{$episode->id}_*",
);
cache()->deleteMatching('page_credits_*');
// delete episode lists cache per year / season for a podcast if ($episode->season_number) {
// and localized pages cache()->deleteMatching("podcast#{$episode->podcast_id}_season*");
$episodeModel = new EpisodeModel(); cache()->deleteMatching(
$years = $episodeModel->getYears($episode->podcast_id); "page_podcast#{$episode->podcast_id}_season*",
$seasons = $episodeModel->getSeasons($episode->podcast_id);
$supportedLocales = config('App')->supportedLocales;
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$episode->podcast->id}_episode{$episode->id}_{$locale}",
); );
cache()->delete("credits_{$locale}"); } else {
} cache()->deleteMatching("podcast#{$episode->podcast_id}_year*");
cache()->deleteMatching(
foreach ($years as $year) { "page_podcast#{$episode->podcast_id}_year*",
cache()->delete(
"podcast{$episode->podcast_id}_year{$year['year']}_episodes",
); );
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$episode->podcast_id}_year{$year['year']}_{$locale}",
);
}
}
foreach ($seasons as $season) {
cache()->delete(
"podcast{$episode->podcast_id}_season{$season['season_number']}_episodes",
);
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$episode->podcast_id}_season{$season['season_number']}_{$locale}",
);
}
}
foreach (array_keys(self::$themes) as $themeKey) {
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$episode->podcast_id}_episode{$episode->id}_embeddable_player_{$themeKey}_{$locale}",
);
}
} }
// delete query cache // delete query cache
cache()->delete("podcast{$episode->podcast_id}_defaultQuery"); cache()->delete("podcast#{$episode->podcast_id}_defaultQuery");
cache()->delete("podcast{$episode->podcast_id}_years"); cache()->delete("podcast#{$episode->podcast_id}_years");
cache()->delete("podcast{$episode->podcast_id}_seasons"); cache()->delete("podcast#{$episode->podcast_id}_seasons");
return $data; return $data;
} }

View File

@ -38,27 +38,17 @@ class EpisodePersonModel extends Model
protected $afterInsert = ['clearCache']; protected $afterInsert = ['clearCache'];
protected $beforeDelete = ['clearCache']; protected $beforeDelete = ['clearCache'];
public function getPersonsByEpisodeId($podcastId, $episodeId) public function getEpisodePersons($episodeId)
{ {
if ( $cacheName = "podcast_episode#{$episodeId}_persons";
!($found = cache( if (!($found = cache($cacheName))) {
"podcast{$podcastId}_episodes{$episodeId}_persons"
))
) {
$found = $this->select('episodes_persons.*') $found = $this->select('episodes_persons.*')
->where('episode_id', $episodeId) ->where('episode_id', $episodeId)
->join( ->join('persons', 'person_id=persons.id')
'persons',
'person_id=persons.id'
)
->orderby('full_name') ->orderby('full_name')
->findAll(); ->findAll();
cache()->save( cache()->save($cacheName, $found, DECADE);
"podcast{$podcastId}_episodes{$episodeId}_persons",
$found,
DECADE
);
} }
return $found; return $found;
} }
@ -81,11 +71,9 @@ class EpisodePersonModel extends Model
) { ) {
if (!empty($persons)) { if (!empty($persons)) {
$this->clearCache([ $this->clearCache([
'id' => [ 'episode_id' => $episodeId,
'podcast_id' => $podcastId,
'episode_id' => $episodeId,
],
]); ]);
$data = []; $data = [];
foreach ($persons as $person) { foreach ($persons as $person) {
if ($groups_roles) { if ($groups_roles) {
@ -126,23 +114,17 @@ class EpisodePersonModel extends Model
protected function clearCache(array $data) protected function clearCache(array $data)
{ {
$podcastId = null;
$episodeId = null; $episodeId = null;
if ( if (isset($data['episode_id'])) {
isset($data['id']['podcast_id']) && $episodeId = $data['episode_id'];
isset($data['id']['episode_id'])
) {
$podcastId = $data['id']['podcast_id'];
$episodeId = $data['id']['episode_id'];
} else { } else {
$episodePerson = (new EpisodePersonModel())->find( $person = (new EpisodePersonModel())->find(
is_array($data['id']) ? $data['id']['id'] : $data['id'] is_array($data['id']) ? $data['id']['id'] : $data['id'],
); );
$podcastId = $episodePerson->podcast_id; $episodeId = $person->episode_id;
$episodeId = $episodePerson->episode_id;
} }
cache()->delete("podcast{$podcastId}_episodes{$episodeId}_persons"); cache()->delete("podcast_episode#{$episodeId}_persons");
(new EpisodeModel())->clearCache(['id' => $episodeId]); (new EpisodeModel())->clearCache(['id' => $episodeId]);
return $data; return $data;

View File

@ -25,7 +25,7 @@ class PageModel extends Model
protected $validationRules = [ protected $validationRules = [
'title' => 'required', 'title' => 'required',
'slug' => 'slug' =>
'required|regex_match[/^[a-zA-Z0-9\-]{1,191}$/]|is_unique[pages.slug,id,{id}]|not_in_protected_slugs', 'required|regex_match[/^[a-zA-Z0-9\-]{1,191}$/]|is_unique[pages.slug,id,{id}]',
'content' => 'required', 'content' => 'required',
]; ];
protected $validationMessages = []; protected $validationMessages = [];
@ -37,46 +37,8 @@ class PageModel extends Model
protected function clearCache(array $data) protected function clearCache(array $data)
{ {
$page = (new PageModel())->find( // Clear the cache of all pages
is_array($data['id']) ? $data['id'][0] : $data['id'], cache()->deleteMatching('page*');
);
// delete page cache
cache()->delete(md5($page->link));
// Clear the cache of all podcast and episode pages
$allPodcasts = (new PodcastModel())->findAll();
foreach ($allPodcasts as $podcast) {
// delete localized podcast and episode page cache
$episodeModel = new EpisodeModel();
$years = $episodeModel->getYears($podcast->id);
$seasons = $episodeModel->getSeasons($podcast->id);
$supportedLocales = config('App')->supportedLocales;
foreach ($years as $year) {
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$podcast->id}_year{$year['year']}_{$locale}",
);
}
}
foreach ($seasons as $season) {
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$podcast->id}_season{$season['season_number']}_{$locale}",
);
}
}
foreach ($podcast->episodes as $episode) {
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$podcast->id}_episode{$episode->id}_{$locale}",
);
}
}
}
return $data; return $data;
} }

View File

@ -48,9 +48,11 @@ class PersonModel extends Model
public function getPersonById($personId) public function getPersonById($personId)
{ {
if (!($found = cache("person{$personId}"))) { $cacheName = "person#{$personId}";
if (!($found = cache($cacheName))) {
$found = $this->find($personId); $found = $this->find($personId);
cache()->save("person{$personId}", $found, DECADE);
cache()->save($cacheName, $found, DECADE);
} }
return $found; return $found;
@ -99,7 +101,8 @@ class PersonModel extends Model
{ {
$options = []; $options = [];
$locale = service('request')->getLocale(); $locale = service('request')->getLocale();
if (!($options = cache("taxonomy_options_{$locale}"))) { $cacheName = "taxonomy_options_{$locale}";
if (!($options = cache($cacheName))) {
foreach (lang('PersonsTaxonomy.persons') as $group_key => $group) { foreach (lang('PersonsTaxonomy.persons') as $group_key => $group) {
foreach ($group['roles'] as $role_key => $role) { foreach ($group['roles'] as $role_key => $role) {
$options[ $options[
@ -108,7 +111,7 @@ class PersonModel extends Model
} }
} }
cache()->save("taxonomy_options_{$locale}", $options, DECADE); cache()->save($cacheName, $options, DECADE);
} }
return $options; return $options;
@ -116,19 +119,15 @@ class PersonModel extends Model
protected function clearCache(array $data) protected function clearCache(array $data)
{ {
$person = (new PersonModel())->getPersonById( $person = (new PersonModel())->find(
is_array($data['id']) ? $data['id'][0] : $data['id'], is_array($data['id']) ? $data['id'][0] : $data['id'],
); );
cache()->delete('person_options'); cache()->delete('person_options');
cache()->delete("person{$person->id}"); cache()->delete("person#{$person->id}");
cache()->delete("user{$person->created_by}_persons");
$supportedLocales = config('App')->supportedLocales; // clear cache for every credits page
// clear cache for every credit page cache()->deleteMatching('page_credits_*');
foreach ($supportedLocales as $locale) {
cache()->delete("credit_{$locale}");
}
return $data; return $data;
} }

View File

@ -45,9 +45,10 @@ class PlatformModel extends Model
public function getPlatform($slug) public function getPlatform($slug)
{ {
if (!($found = cache("platform_$slug"))) { $cacheName = "platform@{$slug}";
if (!($found = cache($cacheName))) {
$found = $this->where('slug', $slug)->first(); $found = $this->where('slug', $slug)->first();
cache()->save("platform_$slug", $found, DECADE); cache()->save($cacheName, $found, DECADE);
} }
return $found; return $found;
} }
@ -72,7 +73,9 @@ class PlatformModel extends Model
public function getPlatformsWithLinks($podcastId, $platformType) public function getPlatformsWithLinks($podcastId, $platformType)
{ {
if ( if (
!($found = cache("podcast{$podcastId}_platforms_{$platformType}")) !($found = cache(
"podcast#{$podcastId}_platforms_{$platformType}_withLinks",
))
) { ) {
$found = $this->select( $found = $this->select(
'platforms.*, podcasts_platforms.link_url, podcasts_platforms.link_content, podcasts_platforms.is_visible, podcasts_platforms.is_on_embeddable_player', 'platforms.*, podcasts_platforms.link_url, podcasts_platforms.link_content, podcasts_platforms.is_visible, podcasts_platforms.is_on_embeddable_player',
@ -86,7 +89,7 @@ class PlatformModel extends Model
->findAll(); ->findAll();
cache()->save( cache()->save(
"podcast{$podcastId}_platforms_{$platformType}", "podcast#{$podcastId}_platforms_{$platformType}_withLinks",
$found, $found,
DECADE, DECADE,
); );
@ -97,11 +100,8 @@ class PlatformModel extends Model
public function getPodcastPlatforms($podcastId, $platformType) public function getPodcastPlatforms($podcastId, $platformType)
{ {
if ( $cacheName = "podcast#{$podcastId}_platforms_{$platformType}";
!($found = cache( if (!($found = cache($cacheName))) {
"podcast{$podcastId}_podcastPlatforms_{$platformType}",
))
) {
$found = $this->select( $found = $this->select(
'platforms.*, podcasts_platforms.link_url, podcasts_platforms.link_content, podcasts_platforms.is_visible, podcasts_platforms.is_on_embeddable_player', 'platforms.*, podcasts_platforms.link_url, podcasts_platforms.link_content, podcasts_platforms.is_visible, podcasts_platforms.is_on_embeddable_player',
) )
@ -113,11 +113,7 @@ class PlatformModel extends Model
->where('platforms.type', $platformType) ->where('platforms.type', $platformType)
->findAll(); ->findAll();
cache()->save( cache()->save($cacheName, $found, DECADE);
"podcast{$podcastId}_podcastPlatforms_{$platformType}",
$found,
DECADE,
);
} }
return $found; return $found;
@ -132,12 +128,14 @@ class PlatformModel extends Model
$podcastsPlatformsTable = $this->db->prefixTable('podcasts_platforms'); $podcastsPlatformsTable = $this->db->prefixTable('podcasts_platforms');
$platformsTable = $this->db->prefixTable('platforms'); $platformsTable = $this->db->prefixTable('platforms');
$deleteJoinQuery = <<<EOD
$deleteJoinQuery = <<<SQL
DELETE $podcastsPlatformsTable DELETE $podcastsPlatformsTable
FROM $podcastsPlatformsTable FROM $podcastsPlatformsTable
INNER JOIN $platformsTable ON $platformsTable.slug = $podcastsPlatformsTable.platform_slug INNER JOIN $platformsTable ON $platformsTable.slug = $podcastsPlatformsTable.platform_slug
WHERE `podcast_id` = ? AND `type` = ? WHERE `podcast_id` = ? AND `type` = ?
EOD; SQL;
$this->db->query($deleteJoinQuery, [$podcastId, $platformType]); $this->db->query($deleteJoinQuery, [$podcastId, $platformType]);
// Set podcastPlatforms // Set podcastPlatforms
@ -168,51 +166,9 @@ class PlatformModel extends Model
public function clearCache($podcastId) public function clearCache($podcastId)
{ {
$podcast = (new PodcastModel())->getPodcastById($podcastId); cache()->deleteMatching("podcast#{$podcastId}_platforms_*");
foreach (['podcasting', 'social', 'funding'] as $platformType) {
cache()->delete("podcast{$podcastId}_platforms_{$platformType}");
cache()->delete(
"podcast{$podcastId}_podcastPlatforms_{$platformType}",
);
}
// delete localized podcast page cache // delete localized podcast page cache
$episodeModel = new EpisodeModel(); cache()->deleteMatching("page_podcast#{$podcastId}*");
$years = $episodeModel->getYears($podcastId);
$seasons = $episodeModel->getSeasons($podcastId);
$supportedLocales = config('App')->supportedLocales;
foreach ($years as $year) {
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$podcastId}_year{$year['year']}_{$locale}",
);
}
}
foreach ($seasons as $season) {
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$podcastId}_season{$season['season_number']}_{$locale}",
);
}
}
// clear cache for every localized podcast episode page
foreach ($podcast->episodes as $episode) {
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$podcast->id}_episode{$episode->id}_{$locale}",
);
foreach (
array_keys(\App\Models\EpisodeModel::$themes)
as $themeKey
) {
cache()->delete(
"page_podcast{$podcast->id}_episode{$episode->id}_embeddable_player_{$themeKey}_{$locale}",
);
}
}
}
} }
} }

View File

@ -83,7 +83,8 @@ class PodcastModel extends Model
public function getPodcastByName($podcastName) public function getPodcastByName($podcastName)
{ {
if (!($found = cache("podcast@{$podcastName}"))) { $cacheName = "podcast@{$podcastName}";
if (!($found = cache($cacheName))) {
$found = $this->where('name', $podcastName)->first(); $found = $this->where('name', $podcastName)->first();
cache()->save("podcast@{$podcastName}", $found, DECADE); cache()->save("podcast@{$podcastName}", $found, DECADE);
} }
@ -93,10 +94,11 @@ class PodcastModel extends Model
public function getPodcastById($podcastId) public function getPodcastById($podcastId)
{ {
if (!($found = cache("podcast{$podcastId}"))) { $cacheName = "podcast#{$podcastId}";
if (!($found = cache($cacheName))) {
$found = $this->find($podcastId); $found = $this->find($podcastId);
cache()->save("podcast{$podcastId}", $found, DECADE); cache()->save($cacheName, $found, DECADE);
} }
return $found; return $found;
@ -111,7 +113,8 @@ class PodcastModel extends Model
*/ */
public function getUserPodcasts($userId) public function getUserPodcasts($userId)
{ {
if (!($found = cache("user{$userId}_podcasts"))) { $cacheName = "user{$userId}_podcasts";
if (!($found = cache($cacheName))) {
$found = $this->select('podcasts.*') $found = $this->select('podcasts.*')
->join( ->join(
'podcasts_users', 'podcasts_users',
@ -120,7 +123,7 @@ class PodcastModel extends Model
->where('podcasts_users.user_id', $userId) ->where('podcasts_users.user_id', $userId)
->findAll(); ->findAll();
cache()->save("user{$userId}_podcasts", $found, DECADE); cache()->save($cacheName, $found, DECADE);
} }
return $found; return $found;
@ -128,7 +131,7 @@ class PodcastModel extends Model
public function addPodcastContributor($userId, $podcastId, $groupId) public function addPodcastContributor($userId, $podcastId, $groupId)
{ {
cache()->delete("podcast{$podcastId}_contributors"); cache()->delete("podcast#{$podcastId}_contributors");
$data = [ $data = [
'user_id' => (int) $userId, 'user_id' => (int) $userId,
@ -141,7 +144,7 @@ class PodcastModel extends Model
public function updatePodcastContributor($userId, $podcastId, $groupId) public function updatePodcastContributor($userId, $podcastId, $groupId)
{ {
cache()->delete("podcast{$podcastId}_contributors"); cache()->delete("podcast#{$podcastId}_contributors");
return $this->db return $this->db
->table('podcasts_users') ->table('podcasts_users')
@ -154,7 +157,7 @@ class PodcastModel extends Model
public function removePodcastContributor($userId, $podcastId) public function removePodcastContributor($userId, $podcastId)
{ {
cache()->delete("podcast{$podcastId}_contributors"); cache()->delete("podcast#{$podcastId}_contributors");
return $this->db return $this->db
->table('podcasts_users') ->table('podcasts_users')
@ -196,75 +199,122 @@ class PodcastModel extends Model
: false; : false;
} }
public function getYears(int $podcastId): array
{
$cacheName = "podcast#{$podcastId}_years";
if (!($found = cache($cacheName))) {
$episodeModel = new EpisodeModel();
$found = $episodeModel
->select(
'YEAR(published_at) as year, count(*) as number_of_episodes',
)
->where([
'podcast_id' => $podcastId,
'season_number' => null,
$episodeModel->deletedField => null,
])
->where('`published_at` <= NOW()', null, false)
->groupBy('year')
->orderBy('year', 'DESC')
->get()
->getResultArray();
$secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode(
$podcastId,
);
cache()->save(
$cacheName,
$found,
$secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
);
}
return $found;
}
public function getSeasons(int $podcastId): array
{
$cacheName = "podcast#{$podcastId}_seasons";
if (!($found = cache($cacheName))) {
$episodeModel = new EpisodeModel();
$found = $episodeModel
->select('season_number, count(*) as number_of_episodes')
->where([
'podcast_id' => $podcastId,
'season_number is not' => null,
$episodeModel->deletedField => null,
])
->where('`published_at` <= NOW()', null, false)
->groupBy('season_number')
->orderBy('season_number', 'ASC')
->get()
->getResultArray();
$secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode(
$podcastId,
);
cache()->save(
$cacheName,
$found,
$secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
);
}
return $found;
}
/**
* Returns the default query for displaying the episode list on the podcast page
*
* @param int $podcastId
*
* @return array|null
*/
public function getDefaultQuery(int $podcastId)
{
$cacheName = "podcast#{$podcastId}_defaultQuery";
if (!($defaultQuery = cache($cacheName))) {
$seasons = $this->getSeasons($podcastId);
if (!empty($seasons)) {
// get latest season
$defaultQuery = ['type' => 'season', 'data' => end($seasons)];
} else {
$years = $this->getYears($podcastId);
if (!empty($years)) {
// get most recent year
$defaultQuery = ['type' => 'year', 'data' => $years[0]];
} else {
$defaultQuery = null;
}
}
cache()->save($cacheName, $defaultQuery, DECADE);
}
return $defaultQuery;
}
public function clearCache(array $data) public function clearCache(array $data)
{ {
$podcast = (new PodcastModel())->getPodcastById( $podcast = (new PodcastModel())->getPodcastById(
is_array($data['id']) ? $data['id'][0] : $data['id'], is_array($data['id']) ? $data['id'][0] : $data['id'],
); );
$supportedLocales = config('App')->supportedLocales;
// delete cache for rss feed and podcast pages // delete cache all podcast pages
cache()->delete("podcast{$podcast->id}_feed"); cache()->deleteMatching("page_podcast#{$podcast->id}_*");
foreach (\Opawg\UserAgentsPhp\UserAgentsRSS::$db as $service) {
cache()->delete("podcast{$podcast->id}_feed_{$service['slug']}");
}
// delete model requests cache // delete model requests cache, includes feed / query / episode lists, etc.
cache()->delete("podcast{$podcast->id}"); cache()->deleteMatching("podcast#{$podcast->id}*");
cache()->delete("podcast@{$podcast->name}"); cache()->delete("podcast@{$podcast->name}");
// clear cache for every localized podcast episode page
foreach ($podcast->episodes as $episode) {
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$podcast->id}_episode{$episode->id}_{$locale}",
);
foreach (
array_keys(\App\Models\EpisodeModel::$themes)
as $themeKey
) {
cache()->delete(
"page_podcast{$podcast->id}_episode{$episode->id}_embeddable_player_{$themeKey}_{$locale}",
);
}
}
}
// clear cache for every credit page // clear cache for every credit page
foreach ($supportedLocales as $locale) { cache()->deleteMatching('page_credits_*');
cache()->delete("credits_{$locale}");
}
// delete episode lists cache per year / season
// and localized pages
$episodeModel = new EpisodeModel();
$years = $episodeModel->getYears($podcast->id);
$seasons = $episodeModel->getSeasons($podcast->id);
foreach ($years as $year) {
cache()->delete(
"podcast{$podcast->id}_year{$year['year']}_episodes",
);
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$podcast->id}_year{$year['year']}_{$locale}",
);
}
}
foreach ($seasons as $season) {
cache()->delete(
"podcast{$podcast->id}_season{$season['season_number']}_episodes",
);
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$podcast->id}_season{$season['season_number']}_{$locale}",
);
}
}
// delete query cache
cache()->delete("podcast{$podcast->id}_defaultQuery");
cache()->delete("podcast{$podcast->id}_years");
cache()->delete("podcast{$podcast->id}_seasons");
return $data; return $data;
} }

View File

@ -37,20 +37,19 @@ class PodcastPersonModel extends Model
protected $afterInsert = ['clearCache']; protected $afterInsert = ['clearCache'];
protected $beforeDelete = ['clearCache']; protected $beforeDelete = ['clearCache'];
public function getPersonsByPodcastId($podcastId) public function getPodcastPersons($podcastId)
{ {
if (!($found = cache("podcast{$podcastId}_persons"))) { $cacheName = "podcast#{$podcastId}_persons";
if (!($found = cache($cacheName))) {
$found = $this->select('podcasts_persons.*') $found = $this->select('podcasts_persons.*')
->where('podcast_id', $podcastId) ->where('podcast_id', $podcastId)
->join( ->join('persons', 'person_id=persons.id')
'persons',
'person_id=persons.id'
)
->orderby('full_name') ->orderby('full_name')
->findAll(); ->findAll();
cache()->save("podcast{$podcastId}_persons", $found, DECADE); cache()->save($cacheName, $found, DECADE);
} }
return $found; return $found;
} }
@ -66,7 +65,7 @@ class PodcastPersonModel extends Model
public function addPodcastPersons($podcastId, $persons, $groups_roles) public function addPodcastPersons($podcastId, $persons, $groups_roles)
{ {
if (!empty($persons)) { if (!empty($persons)) {
$this->clearCache(['id' => ['podcast_id' => $podcastId]]); $this->clearCache(['podcast_id' => $podcastId]);
$data = []; $data = [];
foreach ($persons as $person) { foreach ($persons as $person) {
if ($groups_roles) { if ($groups_roles) {
@ -102,16 +101,16 @@ class PodcastPersonModel extends Model
protected function clearCache(array $data) protected function clearCache(array $data)
{ {
$podcastId = null; $podcastId = null;
if (isset($data['id']['podcast_id'])) { if (isset($data['podcast_id'])) {
$podcastId = $data['id']['podcast_id']; $podcastId = $data['podcast_id'];
} else { } else {
$person = (new PodcastPersonModel())->find( $person = (new PodcastPersonModel())->find(
is_array($data['id']) ? $data['id']['id'] : $data['id'] is_array($data['id']) ? $data['id']['id'] : $data['id'],
); );
$podcastId = $person->podcast_id; $podcastId = $person->podcast_id;
} }
cache()->delete("podcast{$podcastId}_persons"); cache()->delete("podcast#{$podcastId}_persons");
(new PodcastModel())->clearCache(['id' => $podcastId]); (new PodcastModel())->clearCache(['id' => $podcastId]);
return $data; return $data;

View File

@ -56,14 +56,15 @@ class SoundbiteModel extends Model
*/ */
public function getEpisodeSoundbites(int $podcastId, int $episodeId): array public function getEpisodeSoundbites(int $podcastId, int $episodeId): array
{ {
if (!($found = cache("episode{$episodeId}_soundbites"))) { $cacheName = "podcast_episode#{$episodeId}_soundbites";
if (!($found = cache($cacheName))) {
$found = $this->where([ $found = $this->where([
'episode_id' => $episodeId, 'episode_id' => $episodeId,
'podcast_id' => $podcastId, 'podcast_id' => $podcastId,
]) ])
->orderBy('start_time') ->orderBy('start_time')
->findAll(); ->findAll();
cache()->save("episode{$episodeId}_soundbites", $found, DECADE); cache()->save($cacheName, $found, DECADE);
} }
return $found; return $found;
} }
@ -73,25 +74,18 @@ class SoundbiteModel extends Model
$episode = (new EpisodeModel())->find( $episode = (new EpisodeModel())->find(
isset($data['data']) isset($data['data'])
? $data['data']['episode_id'] ? $data['data']['episode_id']
: $data['id']['episode_id'] : $data['id']['episode_id'],
); );
cache()->delete("episode{$episode->id}_soundbites"); cache()->delete("podcast_episode#{$episode->id}_soundbites");
// delete cache for rss feed // delete cache for rss feed
cache()->delete("podcast{$episode->id}_feed"); cache()->deleteMatching("podcast#{$episode->podcast_id}_feed*");
foreach (\Opawg\UserAgentsPhp\UserAgentsRSS::$db as $service) {
cache()->delete( cache()->deleteMatching(
"podcast{$episode->podcast->id}_feed_{$service['slug']}" "page_podcast#{$episode->podcast_id}_episode#{$episode->id}_*",
); );
}
$supportedLocales = config('App')->supportedLocales;
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$episode->podcast->id}_episode{$episode->id}_{$locale}"
);
}
return $data; return $data;
} }
} }

View File

@ -14,7 +14,8 @@ class UserModel extends \Myth\Auth\Models\UserModel
public function getPodcastContributors($podcastId) public function getPodcastContributors($podcastId)
{ {
if (!($found = cache("podcast{$podcastId}_contributors"))) { $cacheName = "podcast#{$podcastId}_contributors";
if (!($found = cache($cacheName))) {
$found = $this->select('users.*, auth_groups.name as podcast_role') $found = $this->select('users.*, auth_groups.name as podcast_role')
->join('podcasts_users', 'podcasts_users.user_id = users.id') ->join('podcasts_users', 'podcasts_users.user_id = users.id')
->join( ->join(
@ -24,7 +25,7 @@ class UserModel extends \Myth\Auth\Models\UserModel
->where('podcasts_users.podcast_id', $podcastId) ->where('podcasts_users.podcast_id', $podcastId)
->findAll(); ->findAll();
cache()->save("podcast{$podcastId}_contributors", $found, DECADE); cache()->save($cacheName, $found, DECADE);
} }
return $found; return $found;

View File

@ -10,26 +10,6 @@ namespace App\Validation;
class Rules class Rules
{ {
/**
* Value should not be within the array of protected slugs (adminGateway, authGateway or installGateway)
*
* @param string $value
*
* @return boolean
*/
public function not_in_protected_slugs(string $value = null): bool
{
$appConfig = config('App');
$protectedSlugs = [
$appConfig->adminGateway,
$appConfig->authGateway,
$appConfig->installGateway,
];
return !in_array($value, $protectedSlugs, true);
}
//--------------------------------------------------------------------
/** /**
* Checks a URL to ensure it's formed correctly. * Checks a URL to ensure it's formed correctly.
* *

View File

@ -21,7 +21,7 @@
[ [
'file' => lang('Install.form.cacheHandlerOptions.file'), 'file' => lang('Install.form.cacheHandlerOptions.file'),
'redis' => lang('Install.form.cacheHandlerOptions.redis'), 'redis' => lang('Install.form.cacheHandlerOptions.redis'),
'memcached' => lang('Install.form.cacheHandlerOptions.memcached'), 'predis' => lang('Install.form.cacheHandlerOptions.predis'),
], ],
old('cache_handler', 'file'), old('cache_handler', 'file'),
[ [

88
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "0d419d654c43fa6e14f8a96b42258a91", "content-hash": "b5d726bdc7252c80c0fd5a6f53de1948",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@ -68,12 +68,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/codeigniter4/CodeIgniter4.git", "url": "https://github.com/codeigniter4/CodeIgniter4.git",
"reference": "425bca14c840c08b935d730c73da372f3d4bdef8" "reference": "dfbc85af9ef408a6654cce6a462c8fdde3ee2446"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/codeigniter4/CodeIgniter4/zipball/425bca14c840c08b935d730c73da372f3d4bdef8", "url": "https://api.github.com/repos/codeigniter4/CodeIgniter4/zipball/dfbc85af9ef408a6654cce6a462c8fdde3ee2446",
"reference": "425bca14c840c08b935d730c73da372f3d4bdef8", "reference": "dfbc85af9ef408a6654cce6a462c8fdde3ee2446",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -83,18 +83,18 @@
"ext-mbstring": "*", "ext-mbstring": "*",
"kint-php/kint": "^3.3", "kint-php/kint": "^3.3",
"laminas/laminas-escaper": "^2.6", "laminas/laminas-escaper": "^2.6",
"php": "^7.3||^8.0", "php": "^7.3 || ^8.0",
"psr/log": "^1.1" "psr/log": "^1.1"
}, },
"require-dev": { "require-dev": {
"codeigniter4/codeigniter4-standard": "^1.0", "codeigniter4/codeigniter4-standard": "^1.0",
"fakerphp/faker": "^1.9", "fakerphp/faker": "^1.9",
"johnkary/phpunit-speedtrap": "^3.3",
"mikey179/vfsstream": "^1.6", "mikey179/vfsstream": "^1.6",
"phpstan/phpstan": "0.12.82", "nexusphp/tachycardia": "^1.0",
"phpstan/phpstan": "0.12.84",
"phpunit/phpunit": "^9.1", "phpunit/phpunit": "^9.1",
"predis/predis": "^1.1", "predis/predis": "^1.1",
"rector/rector": "^0.10", "rector/rector": "0.10.6",
"squizlabs/php_codesniffer": "^3.3" "squizlabs/php_codesniffer": "^3.3"
}, },
"suggest": { "suggest": {
@ -139,7 +139,7 @@
"slack": "https://codeigniterchat.slack.com", "slack": "https://codeigniterchat.slack.com",
"issues": "https://github.com/codeigniter4/CodeIgniter4/issues" "issues": "https://github.com/codeigniter4/CodeIgniter4/issues"
}, },
"time": "2021-03-25T19:47:47+00:00" "time": "2021-04-20T08:40:30+00:00"
}, },
{ {
"name": "composer/ca-bundle", "name": "composer/ca-bundle",
@ -823,16 +823,16 @@
}, },
{ {
"name": "league/commonmark", "name": "league/commonmark",
"version": "1.5.7", "version": "1.5.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/commonmark.git", "url": "https://github.com/thephpleague/commonmark.git",
"reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54" "reference": "08fa59b8e4e34ea8a773d55139ae9ac0e0aecbaf"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/11df9b36fd4f1d2b727a73bf14931d81373b9a54", "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/08fa59b8e4e34ea8a773d55139ae9ac0e0aecbaf",
"reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54", "reference": "08fa59b8e4e34ea8a773d55139ae9ac0e0aecbaf",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -920,7 +920,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2020-10-31T13:49:32+00:00" "time": "2021-03-28T18:51:39+00:00"
}, },
{ {
"name": "league/html-to-markdown", "name": "league/html-to-markdown",
@ -1010,23 +1010,23 @@
}, },
{ {
"name": "maxmind-db/reader", "name": "maxmind-db/reader",
"version": "v1.10.0", "version": "v1.10.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git", "url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git",
"reference": "07f84d969cfc527ce49388558a366ad376f1f35c" "reference": "569bd44d97d30a4ec12c7793a33004a76d4caf18"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/07f84d969cfc527ce49388558a366ad376f1f35c", "url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/569bd44d97d30a4ec12c7793a33004a76d4caf18",
"reference": "07f84d969cfc527ce49388558a366ad376f1f35c", "reference": "569bd44d97d30a4ec12c7793a33004a76d4caf18",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.2" "php": ">=7.2"
}, },
"conflict": { "conflict": {
"ext-maxminddb": "<1.10.0,>=2.0.0" "ext-maxminddb": "<1.10.1,>=2.0.0"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "*", "friendsofphp/php-cs-fixer": "*",
@ -1069,9 +1069,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/maxmind/MaxMind-DB-Reader-php/issues", "issues": "https://github.com/maxmind/MaxMind-DB-Reader-php/issues",
"source": "https://github.com/maxmind/MaxMind-DB-Reader-php/tree/v1.10.0" "source": "https://github.com/maxmind/MaxMind-DB-Reader-php/tree/v1.10.1"
}, },
"time": "2021-02-09T17:52:47+00:00" "time": "2021-04-14T17:49:35+00:00"
}, },
{ {
"name": "maxmind/web-service-common", "name": "maxmind/web-service-common",
@ -1125,20 +1125,20 @@
}, },
{ {
"name": "michalsn/codeigniter4-uuid", "name": "michalsn/codeigniter4-uuid",
"version": "v1.0.0-beta2", "version": "v1.0.0-beta3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/michalsn/codeigniter4-uuid.git", "url": "https://github.com/michalsn/codeigniter4-uuid.git",
"reference": "a5f9751570a3b27e81deaa7548eef507499be888" "reference": "568aba8f315199b6cc87e76b8441cd03a2bba5b4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/michalsn/codeigniter4-uuid/zipball/a5f9751570a3b27e81deaa7548eef507499be888", "url": "https://api.github.com/repos/michalsn/codeigniter4-uuid/zipball/568aba8f315199b6cc87e76b8441cd03a2bba5b4",
"reference": "a5f9751570a3b27e81deaa7548eef507499be888", "reference": "568aba8f315199b6cc87e76b8441cd03a2bba5b4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.2", "php": ">=7.3",
"ramsey/uuid": "^4.0" "ramsey/uuid": "^4.0"
}, },
"require-dev": { "require-dev": {
@ -1172,9 +1172,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/michalsn/codeigniter4-uuid/issues", "issues": "https://github.com/michalsn/codeigniter4-uuid/issues",
"source": "https://github.com/michalsn/codeigniter4-uuid/tree/develop" "source": "https://github.com/michalsn/codeigniter4-uuid/tree/v1.0.0-beta3"
}, },
"time": "2020-08-16T07:56:20+00:00" "time": "2021-04-02T11:08:18+00:00"
}, },
{ {
"name": "myth/auth", "name": "myth/auth",
@ -1182,12 +1182,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/lonnieezell/myth-auth.git", "url": "https://github.com/lonnieezell/myth-auth.git",
"reference": "a11dc6369177c932add936f1be3844c30fe45ed4" "reference": "eff9805d7f1d27326f14875b53ff4b3d2a6b72ee"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/lonnieezell/myth-auth/zipball/a11dc6369177c932add936f1be3844c30fe45ed4", "url": "https://api.github.com/repos/lonnieezell/myth-auth/zipball/eff9805d7f1d27326f14875b53ff4b3d2a6b72ee",
"reference": "a11dc6369177c932add936f1be3844c30fe45ed4", "reference": "eff9805d7f1d27326f14875b53ff4b3d2a6b72ee",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1248,7 +1248,7 @@
"type": "patreon" "type": "patreon"
} }
], ],
"time": "2021-03-23T03:55:24+00:00" "time": "2021-04-12T22:34:12+00:00"
}, },
{ {
"name": "opawg/user-agents-php", "name": "opawg/user-agents-php",
@ -1359,16 +1359,16 @@
}, },
{ {
"name": "phpseclib/phpseclib", "name": "phpseclib/phpseclib",
"version": "2.0.30", "version": "2.0.31",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpseclib/phpseclib.git", "url": "https://github.com/phpseclib/phpseclib.git",
"reference": "136b9ca7eebef78be14abf90d65c5e57b6bc5d36" "reference": "233a920cb38636a43b18d428f9a8db1f0a1a08f4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/136b9ca7eebef78be14abf90d65c5e57b6bc5d36", "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/233a920cb38636a43b18d428f9a8db1f0a1a08f4",
"reference": "136b9ca7eebef78be14abf90d65c5e57b6bc5d36", "reference": "233a920cb38636a43b18d428f9a8db1f0a1a08f4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1448,7 +1448,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/phpseclib/phpseclib/issues", "issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/2.0.30" "source": "https://github.com/phpseclib/phpseclib/tree/2.0.31"
}, },
"funding": [ "funding": [
{ {
@ -1464,7 +1464,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2020-12-17T05:42:04+00:00" "time": "2021-04-06T13:56:45+00:00"
}, },
{ {
"name": "podlibre/ipcat", "name": "podlibre/ipcat",
@ -2761,16 +2761,16 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "9.2.5", "version": "9.2.6",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1" "reference": "f6293e1b30a2354e8428e004689671b83871edde"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f3e026641cc91909d421802dd3ac7827ebfd97e1", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde",
"reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1", "reference": "f6293e1b30a2354e8428e004689671b83871edde",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2826,7 +2826,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.5" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6"
}, },
"funding": [ "funding": [
{ {
@ -2834,7 +2834,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2020-11-28T06:44:49+00:00" "time": "2021-03-28T07:26:59+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",