Skip to content

Commit 24b2ef6

Browse files
authored
Laravel cache integration (#5)
* Add support for Laravel cache adapters * Preserve uri_protocol during cache manager setup
1 parent 74926ec commit 24b2ef6

File tree

5 files changed

+384
-2
lines changed

5 files changed

+384
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
### Added
66

7+
- The ability for ExpressionEngine to utilize Laravel cache adapters
78
- Artisan command to proxy ExpressionEngine's `eecli` commands in the context of Laravel and Coilpack after they have fully booted. This command is available through `php artisan eecli`.
89
- Parameter to Channel Entries Tag for eager loading relationships. This allows for easier inclusion of custom field data with `exp.channel.entries({with: 'data'})`
10+
911
### Fixed
1012

1113
- Behavior of File Modifier to return original data when manipulation fails silently

src/Bootstrap/LoadExpressionEngine.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ class LoadExpressionEngine
2323
LoadConfiguration::class,
2424
CreateDatabaseConnection::class,
2525
ConfigureStorageDisk::class,
26-
ConfigureAuthProvider::class,
2726
];
2827

2928
public function __construct()
@@ -247,7 +246,6 @@ public function bootstrap(Application $app)
247246

248247
if (! $this->constants['INSTALL_MODE']) {
249248
if ($this->constants['REQ'] !== 'CP') {
250-
ee()->load->library('core');
251249
ee()->core->bootstrap();
252250
} else {
253251
ee()->load->database();
@@ -283,9 +281,14 @@ public function bootstrapDependencies($app)
283281
{
284282
$dependencies = [];
285283

284+
if (! $this->constants['INSTALL_MODE'] && ! in_array($this->constants['REQ'], ['ASSET'])) {
285+
$dependencies[] = SetupCacheManager::class;
286+
}
287+
286288
if (! in_array($this->constants['REQ'], ['CP', 'ASSET'])) {
287289
$dependencies[] = LoadAddonFiles::class;
288290
$dependencies[] = ReplaceTemplateTags::class;
291+
$dependencies[] = ConfigureAuthProvider::class;
289292
}
290293

291294
$app->bootstrapWith(array_merge($this->dependentBootstrappers, $dependencies));
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace Expressionengine\Coilpack\Bootstrap;
4+
5+
use Illuminate\Contracts\Foundation\Application;
6+
use Illuminate\Support\Arr;
7+
use Illuminate\Support\Str;
8+
9+
class SetupCacheManager
10+
{
11+
/**
12+
* Bootstrap the given application.
13+
*
14+
* @return void
15+
*/
16+
public function bootstrap(Application $app)
17+
{
18+
$path = config('coilpack.base_path');
19+
$absolute = (Str::startsWith($path, DIRECTORY_SEPARATOR));
20+
$basePath = Str::finish($absolute ? $path : base_path($path), DIRECTORY_SEPARATOR);
21+
22+
if (! realpath($basePath) || (ee()->has('cache') && ee()->cache instanceof \Expressionengine\Coilpack\CacheManager)) {
23+
return;
24+
// throw new \Exception('ExpressionEngine folder missing.');
25+
}
26+
27+
$overrideConfig = Arr::only(ee()->config->config, [
28+
'uri_protocol',
29+
'directory_trigger',
30+
'controller_trigger',
31+
'function_trigger',
32+
'enable_query_strings',
33+
]);
34+
35+
ee()->core->bootstrap();
36+
37+
// we need these variables to stay in the config after bootstrapping
38+
foreach ($overrideConfig as $key => $value) {
39+
ee()->config->set_item($key, $value);
40+
}
41+
42+
$cache = new \Expressionengine\Coilpack\CacheManager(ee()->cache);
43+
44+
ee()->remove('cache');
45+
ee()->set('cache', $cache);
46+
}
47+
}

src/CacheDriver.php

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
<?php
2+
3+
namespace Expressionengine\Coilpack;
4+
5+
use Cache;
6+
use Illuminate\Contracts\Cache\Repository as CacheRepository;
7+
8+
class CacheDriver
9+
{
10+
protected $repository;
11+
12+
public function __construct(CacheRepository $repository)
13+
{
14+
$this->repository = $repository;
15+
}
16+
17+
/**
18+
* Look for a value in the cache. If it exists, return the data
19+
* if not, return FALSE
20+
*
21+
* @param string $key Key name
22+
* @param const $scope Cache::LOCAL_SCOPE or Cache::GLOBAL_SCOPE for
23+
* local or global scoping of the cache item
24+
* @return mixed value matching $key or FALSE on failure
25+
*/
26+
public function get($key, $scope = Cache::LOCAL_SCOPE)
27+
{
28+
$repository = $this->repository;
29+
30+
if ($repository->supportsTags() && $scope === Cache::LOCAL_SCOPE) {
31+
$repository = $repository->tags($this->_namespaced_key('', $scope));
32+
} else {
33+
$key = $this->_namespaced_key($key, $scope);
34+
}
35+
36+
return $repository->has($key) ? $this->unwrapData($repository->get($key)) : false;
37+
}
38+
39+
/**
40+
* Save value to cache
41+
*
42+
* @param string $key Key name
43+
* @param mixed $data Data to store
44+
* @param int $ttl = 60 Cache TTL (in seconds)
45+
* @param const $scope Cache::LOCAL_SCOPE or Cache::GLOBAL_SCOPE for
46+
* local or global scoping of the cache item
47+
* @return bool TRUE on success, FALSE on failure
48+
*/
49+
public function save($key, $data, $ttl = 60, $scope = Cache::LOCAL_SCOPE)
50+
{
51+
// In ExpressionEngine $ttl = 0 means cache forever
52+
// but in Laravel this means remove from cache, so we will translate to null
53+
$ttl = ((int) $ttl <= 0) ? null : (int) $ttl;
54+
55+
if ($this->repository->supportsTags() && $scope === Cache::LOCAL_SCOPE) {
56+
return $this->repository->tags($this->_namespaced_key('', $scope))->put($key, $this->wrapData($data, $ttl), $ttl);
57+
}
58+
59+
return $this->repository->put($this->_namespaced_key($key, $scope), $this->wrapData($data, $ttl), $ttl);
60+
}
61+
62+
/**
63+
* Delete from cache
64+
*
65+
* To clear a particular namespace, pass in the namespace with a trailing
66+
* slash like so:
67+
*
68+
* ee()->cache->delete('/namespace_name/');
69+
*
70+
* @param string $key Key name
71+
* @param const $scope Cache::LOCAL_SCOPE or Cache::GLOBAL_SCOPE for
72+
* local or global scoping of the cache item
73+
* @return bool TRUE on success, FALSE on failure
74+
*/
75+
public function delete($key, $scope = Cache::LOCAL_SCOPE)
76+
{
77+
if ($this->repository->supportsTags() && $scope === Cache::LOCAL_SCOPE) {
78+
return $this->repository->tags($this->_namespaced_key('', $scope))->forget($key);
79+
}
80+
81+
return $this->repository->forget($this->_namespaced_key($key, $scope));
82+
}
83+
84+
/**
85+
* Clean the cache
86+
*
87+
* @param const $scope Cache::LOCAL_SCOPE or Cache::GLOBAL_SCOPE for
88+
* local or global scoping of the cache item
89+
* @return bool TRUE on success, FALSE on failure
90+
*/
91+
public function clean($scope = Cache::LOCAL_SCOPE)
92+
{
93+
if ($this->repository->supportsTags() && $scope === Cache::LOCAL_SCOPE) {
94+
return $this->repository->tags($this->_namespaced_key('', $scope))->clear();
95+
}
96+
97+
return $this->repository->clear();
98+
}
99+
100+
/**
101+
* Cache Info
102+
*
103+
* @return mixed array containing cache info on success OR FALSE on failure
104+
*/
105+
public function cache_info()
106+
{
107+
return null;
108+
}
109+
110+
/**
111+
* Get Cache Metadata
112+
*
113+
* @param string $key Key to get cache metadata on
114+
* @param const $scope Cache::LOCAL_SCOPE or Cache::GLOBAL_SCOPE for
115+
* local or global scoping of the cache item
116+
* @return mixed cache item metadata
117+
*/
118+
public function get_metadata($key, $scope = Cache::LOCAL_SCOPE)
119+
{
120+
$repository = $this->repository;
121+
122+
if ($repository->supportsTags() && $scope === Cache::LOCAL_SCOPE) {
123+
$repository = $repository->tags($this->_namespaced_key('', $scope));
124+
} else {
125+
$key = $this->_namespaced_key($key, $scope);
126+
}
127+
128+
if (! $repository->has($key)) {
129+
return false;
130+
}
131+
132+
$data = $repository->get($key);
133+
134+
$data = is_array($data) ? $data : compact('data');
135+
$data = array_merge(array_fill_keys(['ttl', 'time', 'data'], null), $data);
136+
137+
// Convert a 'forever ttl' of null back to 0 for ExpressionEngine
138+
$data['expire'] = $data['time'] ? ($data['time'] + $data['ttl'] ?: 0) : null;
139+
$data['mtime'] = $data['time'];
140+
141+
return $data;
142+
}
143+
144+
/**
145+
* Wrap $data in an array with extra metadata to store
146+
*
147+
* @param mixed $data
148+
* @param mixed $ttl
149+
* @return array
150+
*/
151+
protected function wrapData($data, $ttl)
152+
{
153+
return [
154+
'time' => ee()->localize->now,
155+
'ttl' => $ttl,
156+
'data' => $data,
157+
];
158+
}
159+
160+
/**
161+
* Extract the data key from a given $data array
162+
*
163+
* @param mixed $data
164+
* @return mixed
165+
*/
166+
protected function unwrapData($data)
167+
{
168+
return (is_array($data) && array_key_exists('data', $data)) ? $data['data'] : $data;
169+
}
170+
171+
/**
172+
* If a namespace was specified, prefixes the key with it
173+
*
174+
* For the file driver, namespaces will be actual folders
175+
*
176+
* @param string $key Key name
177+
* @param const $scope Cache::LOCAL_SCOPE or Cache::GLOBAL_SCOPE
178+
* for local or global scoping of the cache item
179+
* @return string Key prefixed with namespace
180+
*/
181+
protected function _namespaced_key($key, $scope = Cache::LOCAL_SCOPE)
182+
{
183+
// Make sure the key doesn't begin or end with a namespace separator or
184+
// directory separator to force the last segment of the key to be the
185+
// file name and so we can prefix a directory reliably
186+
$key = trim($key, Cache::NAMESPACE_SEPARATOR.DIRECTORY_SEPARATOR);
187+
188+
// Sometime class names are used as keys, replace class namespace
189+
// slashes with underscore to prevent filesystem issues
190+
$key = str_replace('\\', '_', $key);
191+
192+
// Replace all namespace separators with the system's directory separator
193+
$key = str_replace(Cache::NAMESPACE_SEPARATOR, DIRECTORY_SEPARATOR, $key);
194+
195+
// For locally-cached items, separate by site name
196+
if ($scope == Cache::LOCAL_SCOPE) {
197+
$key = (! empty(ee()->config->item('site_short_name')) ? ee()->config->item('site_short_name').DIRECTORY_SEPARATOR : '').$key;
198+
}
199+
200+
return $key;
201+
}
202+
}

0 commit comments

Comments
 (0)