Skip to content

Commit

Permalink
NEW Provide a consistent way of triggering flush
Browse files Browse the repository at this point in the history
Provides an interface for classes to implement their own flush()
functionality. This function gets called early in a request on
all implementations of Flushable when flush=1|all is requested in the
URL.

This fix came out of an issue where Requirements combined files were not
being cleaned up after dev/build?flush=1, due to the fact that flush
would only occur when you called it while on a page that used those
combined files, but not in any other contexts. This will now call flush
on any implementors of Flushable regardless of the context of where
flush was called.
  • Loading branch information
Sean Harvey committed Aug 21, 2014
1 parent 66bacc6 commit 2b316e7
Show file tree
Hide file tree
Showing 15 changed files with 244 additions and 61 deletions.
1 change: 1 addition & 0 deletions _config/requestprocessors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ Injector:
RequestProcessor:
properties:
filters:
- '%$FlushRequestFilter'
- '%$VersionedRequestFilter'
17 changes: 15 additions & 2 deletions api/RestfulService.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* @package framework
* @subpackage integration
*/
class RestfulService extends ViewableData {
class RestfulService extends ViewableData implements Flushable {

protected $baseURL;
protected $queryString;
Expand All @@ -33,6 +33,19 @@ class RestfulService extends ViewableData {
*/
private static $default_curl_options = array();

/**
* @config
* @var bool Flushes caches if set to true. This is set by {@link flush()}
*/
private static $flush = false;

/**
* Triggered early in the request when someone requests a flush.
*/
public static function flush() {
self::$flush = true;
}

/**
* set a curl option that will be applied to all requests as default
* {@see http://php.net/manual/en/function.curl-setopt.php#refsect1-function.curl-setopt-parameters}
Expand Down Expand Up @@ -171,7 +184,7 @@ public function request($subURL = '', $method = "GET", $data = null, $headers =
// Check for unexpired cached feed (unless flush is set)
//assume any cache_expire that is 0 or less means that we dont want to
// cache
if($this->cache_expire > 0 && !isset($_GET['flush'])
if($this->cache_expire > 0 && self::$flush
&& @file_exists($cache_path)
&& @filemtime($cache_path) + $this->cache_expire > time()) {

Expand Down
24 changes: 24 additions & 0 deletions control/FlushRequestFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
/**
* Triggers a call to flush() on all implementors of Flushable.
*
* @package framework
* @subpackage control
*/
class FlushRequestFilter implements RequestFilter {

public function preRequest(SS_HTTPRequest $request, Session $session, DataModel $model) {
if($request->getVar('flush')) {
foreach(ClassInfo::implementorsOf('Flushable') as $class) {
$class::flush();
}
}

return true;
}

public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model) {
return true;
}

}
2 changes: 1 addition & 1 deletion control/RequestProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response,
}
}
}
}
}
20 changes: 20 additions & 0 deletions core/Flushable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
/**
* Provides an interface for classes to implement their own flushing functionality
* whenever flush=1 is requested.
*
* @package framework
* @subpackage core
*/
interface Flushable {

/**
* This function is triggered early in the request if the "flush" query
* parameter has been set. Each class that implements Flushable implements
* this function which looks after it's own specific flushing functionality.
*
* @see FlushRequestFilter
*/
public static function flush();

}
27 changes: 12 additions & 15 deletions dev/TestRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class TestRunner extends Controller {
'build',
'only'
);

/**
* @var Array Blacklist certain directories for the coverage report.
* Filepaths are relative to the webroot, without leading slash.
Expand All @@ -62,7 +62,7 @@ class TestRunner extends Controller {
'*/tests',
'*/lang',
);

/**
* Override the default reporter with a custom configured subclass.
*
Expand All @@ -78,21 +78,26 @@ public static function set_reporter($reporter) {
* top of the loader stacks.
*/
public static function use_test_manifest() {
$flush = true;
if(isset($_GET['flush']) && $_GET['flush'] === '0') {
$flush = false;
}

$classManifest = new SS_ClassManifest(
BASE_PATH, true, isset($_GET['flush'])
BASE_PATH, true, $flush
);

SS_ClassLoader::instance()->pushManifest($classManifest, false);
SapphireTest::set_test_class_manifest($classManifest);

SS_TemplateLoader::instance()->pushManifest(new SS_TemplateManifest(
BASE_PATH, project(), true, isset($_GET['flush'])
BASE_PATH, project(), true, $flush
));

Config::inst()->pushConfigStaticManifest(new SS_ConfigStaticManifest(
BASE_PATH, true, isset($_GET['flush'])
BASE_PATH, true, $flush
));

// Invalidate classname spec since the test manifest will now pull out new subclasses for each internal class
// (e.g. Member will now have various subclasses of DataObjects that implement TestOnly)
DataObject::clear_classname_spec_cache();
Expand All @@ -109,14 +114,6 @@ public function init() {
if(!PhpUnitWrapper::has_php_unit()) {
die("Please install PHPUnit using pear");
}

if(!isset($_GET['flush']) || !$_GET['flush']) {
Debug::message(
"WARNING: Manifest not flushed. " .
"Add flush=1 as an argument to discover new classes or files.\n",
false
);
}
}

public function Link() {
Expand Down
20 changes: 17 additions & 3 deletions docs/en/howto/phpunit-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,31 @@ Example `mysite/_config.php`:
if($db == 'sqlite3') $databaseConfig['type'] = 'SQLite3Database';
}
}

You can either use the database on a single invocation:

phpunit framework/tests "" db=sqlite3

or through a `<php>` flag in your `phpunit.xml` (see [Appenix C: "Setting PHP INI settings"](http://www.phpunit.de/manual/current/en/appendixes.configuration.html)):

<phpunit>
<!-- ... -->
<php>
<var name="db" value="sqlite3"/>
<get name="db" value="sqlite3"/>
</php>
</phpunit>

Note that on every test run, the manifest is flushed to avoid any bugs where a test doesn't clean up after
itself properly. You can override that behaviour by passing `flush=0` to the test command:

phpunit framework/tests flush=0

Alternatively, you can set the var in your `phpunit.xml` file:

<phpunit>
<!-- ... -->
<php>
<get name="flush" value="0"/>
</php>
</phpunit>

Expand Down
9 changes: 9 additions & 0 deletions docs/en/reference/execution-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,12 @@ You can access the following controller-method with /team/signup
## SSViewer template rendering

See [templates](/reference/templates) for information on the SSViewer template system.

## Flush requests

If `?flush=1` is requested in the URL, e.g. http://mysite.com?flush=1, this will trigger a call to `flush()` on
any classes that implement the `Flushable` interface.

See [reference documentation on Flushable](/reference/flushable) for more details.


56 changes: 56 additions & 0 deletions docs/en/reference/flushable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Flushable

## Introduction

Allows a class to define it's own flush functionality, which is triggered when `flush=1` is requested in the URL.

`[api:FlushRequestFilter]` is run before a request is made, calling `flush()` statically on all
implementors of `[api:Flushable]`.

## Usage

To use this API, you need to make your class implement `[api:Flushable]`, and define a `flush()` static function,
this defines the actions that need to be executed on a flush request.

### Using with SS_Cache

This example uses `[api:SS_Cache]` in some custom code, and the same cache is cleaned on flush:

:::php
<?php
class MyClass extends DataObject implements Flushable {

public static function flush() {
SS_Cache::factory('mycache')->clean(Zend_Cache::CLEANING_MODE_ALL);
}

public function MyCachedContent() {
$cache = SS_Cache::factory('mycache')
$something = $cache->get('mykey');
if(!$something) {
$something = 'value to be cached';
$cache->save($something);
}
return $something;
}

}

### Using with filesystem

Another example, some temporary files are created in a directory in assets, and are deleted on flush. This would be
useful in an example like `GD` or `Imagick` generating resampled images, but we want to delete any cached images on
flush so they are re-created on demand.

:::php
<?php
class MyClass extends DataObject implements Flushable {

public static function flush() {
foreach(glob(ASSETS_PATH . '/_tempfiles/*.jpg') as $file) {
unlink($file);
}
}

}

9 changes: 8 additions & 1 deletion docs/en/topics/caching.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ this is reasonably quick, but still requires access to slow disk I/O.
The `Zend_Cache` API supports various caching backends ([list](http://framework.zend.com/manual/1.12/en/zend.cache.backends.html))
which can provide better performance, including APC, Xcache, ZendServer, Memcached and SQLite.

## Cleaning caches on flush=1 requests

If `?flush=1` is requested in the URL, e.g. http://mysite.com?flush=1, this will trigger a call to `flush()` on
any classes that implement the `Flushable` interface. Using this, you can trigger your caches to clean.

See [reference documentation on Flushable](/reference/flushable) for implementation details.

### Memcached

This backends stores cache records into a [memcached](http://www.danga.com/memcached/)
Expand Down Expand Up @@ -144,4 +151,4 @@ a fast one (but limited) like Apc, Memcache... and a "slow" one like File or Sql
'cache_dir' => TEMP_FOLDER . DIRECTORY_SEPARATOR . 'cache'
)
));
SS_Cache::pick_backend('two_level', 'any', 10);
SS_Cache::pick_backend('two_level', 'any', 10);
31 changes: 22 additions & 9 deletions i18n/i18n.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
* @package framework
* @subpackage misc
*/
class i18n extends Object implements TemplateGlobalProvider {
class i18n extends Object implements TemplateGlobalProvider, Flushable {

/**
* This static variable is used to store the current defined locale.
Expand Down Expand Up @@ -97,6 +97,21 @@ class i18n extends Object implements TemplateGlobalProvider {
*/
protected static $translators;

/**
* Triggered early in the request when someone requests a flush.
*/
public static function flush() {
self::get_cache()->clean(Zend_Cache::CLEANING_MODE_ALL);
}

/**
* Return an instance of the cache used for i18n data.
* @return Zend_Cache
*/
public static function get_cache() {
return SS_Cache::factory('i18n', 'Output', array('lifetime' => null, 'automatic_serialization' => true));
}

/**
* Use javascript i18n through the ss.i18n class (enabled by default).
* If set to TRUE, includes javascript requirements for the base library
Expand Down Expand Up @@ -2039,11 +2054,11 @@ public static function _t($entity, $string = "", $context = "", $injection = "")
// which is instanciated by core with a $clean instance variable.

if(!$adapter->isAvailable($lang)) {
i18n::include_by_locale($lang, (isset($_GET['flush'])));
i18n::include_by_locale($lang);
}

if(!$adapter->isAvailable($locale)) {
i18n::include_by_locale($locale, (isset($_GET['flush'])));
i18n::include_by_locale($locale);
}

$translation = $adapter->translate($entity, $locale);
Expand Down Expand Up @@ -2107,9 +2122,7 @@ function($matches) use(&$injectionArray) {
*/
public static function get_translators() {
if(!Zend_Translate::getCache()) {
Zend_Translate::setCache(
SS_Cache::factory('i18n', 'Output', array('lifetime' => null, 'automatic_serialization' => true))
);
Zend_Translate::setCache(self::get_cache());
}

if(!self::$translators) {
Expand All @@ -2122,8 +2135,8 @@ public static function get_translators() {
))
);

i18n::include_by_locale('en', isset($_GET['flush']));
i18n::include_by_locale('en_US', isset($_GET['flush']));
i18n::include_by_locale('en');
i18n::include_by_locale('en_US');
}

return self::$translators;
Expand Down Expand Up @@ -2515,7 +2528,7 @@ public static function get_script_direction($locale = null) {
*/
public static function include_by_locale($locale, $clean = false) {
if($clean) {
Zend_Translate::clearCache();
self::flush();
}

// Get list of module => path pairs, and then just the names
Expand Down
Loading

0 comments on commit 2b316e7

Please sign in to comment.