Skip to content

Commit

Permalink
Adding a command cache.
Browse files Browse the repository at this point in the history
  • Loading branch information
grasmash committed May 17, 2017
1 parent de01106 commit 360d5a7
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 3 deletions.
10 changes: 8 additions & 2 deletions src/Robo/Blt.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Acquia\Blt\Robo;

use Acquia\Blt\Robo\Common\Executor;
use Acquia\Blt\Robo\Datastore\FileStore;
use Acquia\Blt\Robo\Filesets\FilesetManager;
use Acquia\Blt\Robo\Inspector\Inspector;
use Acquia\Blt\Robo\Inspector\InspectorAwareInterface;
Expand Down Expand Up @@ -170,7 +171,7 @@ private function addDefaultArgumentsAndOptions(Application $app) {
/**
* Register the necessary classes for BLT.
*/
public static function configureContainer($container) {
public function configureContainer($container) {
$container->share('logStyler', BltLogStyle::class);

// We create our own builder so that non-command classes are able to
Expand All @@ -197,10 +198,15 @@ public static function configureContainer($container) {

$container->share('filesetManager', FilesetManager::class);

// Install our command cache into the command factory
$commandCacheDir = $this->getConfig()->get('command_cache_dir');
$commandCacheDataStore = new FileStore($commandCacheDir);
/** @var \Consolidation\AnnotatedCommand\AnnotatedCommandFactory $factory */
$factory = $container->get('commandFactory');
// Tell the command loader to only allow command functions that have a
// name/alias.
$factory = $container->get('commandFactory');
$factory->setIncludeAllPublicMethods(FALSE);
$factory->setDataStore($commandCacheDataStore);
}

/**
Expand Down
46 changes: 46 additions & 0 deletions src/Robo/Datastore/DataStoreInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Acquia\Blt\Robo\Datastore;

/**
* Interface DataStoreInterface
*/
interface DataStoreInterface
{
/**
* Reads retrieves data from the store
*
* @param string $key A key
* @return mixed The value fpr the given key or null.
*/
public function get($key);

/**
* Saves a value with the given key
*
* @param string $key A key
* @param mixed $data Data to save to the store
*/
public function set($key, $data);

/**
* Checks if a key is in the store
*
* @param string $key A key
* @return bool Whether a value exists with the given key
*/
public function has($key);

/**
* Remove value from the store
*
* @param string $key A key
*/
public function remove($key);

/**
* Return a list of all keys in the store.
* @return array A list of keys
*/
public function keys();
}
150 changes: 150 additions & 0 deletions src/Robo/Datastore/FileStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php

namespace Acquia\Blt\Robo\Datastore;

/**
* Class FileStore.
*/
class FileStore implements DataStoreInterface
{
/**
* @var string The directory to store the data files in.
*/
protected $directory;

public function __construct($directory)
{
$this->directory = $directory;
}

/**
* Reads retrieves data from the store
*
* @param string $key A key
* @return mixed The value fpr the given key or null.
*/
public function get($key)
{
$out = null;
// Read the json encoded value from disk if it exists.
$path = $this->getFileName($key);
if (file_exists($path)) {
$out = file_get_contents($path);
$out = json_decode($out, true);
}
return $out;
}

/**
* Saves a value with the given key
*
* @param string $key A key
* @param mixed $data Data to save to the store
*/
public function set($key, $data)
{
$path = $this->getFileName($key, true);
file_put_contents($path, json_encode($data));
}

/**
* Checks if a key is in the store
*
* @param string $key A key
* @return bool Whether a value exists with the given key.
*/
public function has($key)
{
$path = $this->getFileName($key);
return file_exists($path);
}

/**
* Remove value from the store
*
* @param string $key A key
*/
public function remove($key)
{
$path = $this->getFileName($key, true);
if (file_exists($path)) {
unlink($path);
}
}

/**
* Remove all values from the store
*/
public function removeAll()
{
foreach ($this->keys() as $key) {
$this->remove($key);
}
}

/**
* Return a list of all keys in the store.
* @return array A list of keys
*/
public function keys()
{
$root = $this->directory;
if (file_exists($root) && is_readable($root)) {
return array_diff(scandir($root), array('..', '.'));
}
return [];
}

/**
* Get a valid file name for the given key.
* @param string $key The data key to be written or read
* @return string A file path
* @throws \Exception
*/
protected function getFileName($key, $writable = false)
{
$key = $this->cleanKey($key);

if ($writable) {
$this->ensureDirectoryWritable();
}

if (!$key) {
throw new \Exception('Could not save data to a file because it is missing an ID');
}
return $this->directory . '/' . $key;
}

/**
* Make the file path safe by whitelisting characters.
* This is a very naive approach to hashing but in practice this doesn't matter since this is only used for a
* few already safe keys.
*
* @param $key
* @return mixed
*/
protected function cleanKey($key)
{
return preg_replace('/[^a-zA-Z0-9\-\_\@\.]/', '-', $key);
}

/**
* Check that the directory is writable and create it if we can.
*/
protected function ensureDirectoryWritable()
{
// Reality check to prevent stomping on the local filesystem if there is something wrong with the config.
if (!$this->directory) {
throw new \Exception('Could not save data to a file because the path setting is mis-configured.');
}

$writable = is_dir($this->directory) || (!file_exists($this->directory) && @mkdir($this->directory, 0777, true));
$writable = $writable && is_writable($this->directory);
if (!$writable) {
throw new \Exception(
'Could not save data to a file because the path {path} cannot be written to.',
['path' => $this->directory]
);
}
}
}
2 changes: 1 addition & 1 deletion src/Robo/Tasks/LoadTasks.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
trait LoadTasks {

/**
* @return DrushTask
* @return \Acquia\Blt\Robo\Tasks\DrushTask
*/
protected function taskDrush() {
return $this->task(DrushTask::class);
Expand Down

0 comments on commit 360d5a7

Please sign in to comment.