Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ jobs:
dist: trusty
- php: 5.4
dist: trusty
- php: 5.3.29
dist: precise

install:
- if [[ ${TRAVIS_PHP_VERSION:0:3} < "7.1" ]]; then rm composer.lock; fi
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"require-dev": {
"mikey179/vfsstream": "^1.6",
"mockery/mockery": "1.* || 0.*",
"mustangostang/spyc": "^0.6",
"mustangostang/spyc": "dev-master#eba310c",
"phpunit/phpunit": "7.* || 4.*"
},
"suggest": {
Expand Down
10 changes: 6 additions & 4 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 28 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,13 @@ $posts = $repo->query()

## Config options

- `formatter`. See [Formats](https://github.com/jamesmoss/flywheel#formats) section of this readme. Defaults to an
- `formatter`. See [Formats](#formats) section of this readme. Defaults to an
instance of `JamesMoss\Flywheel\Formatter\JSON`.
- `query_class`. The name of the class that gets returned from `Repository::query()`. By default, Flywheel detects
if you have APC or APCu installed and uses `CachedQuery` class if applicable, otherwise it just uses `Query`.
- `document_class`. The name of the class to use when hydrating documenst from the filesystem. Must implement `JamesMoss\Flywheel\DocumentInterface`. Defaults to `JamesMoss\Flywheel\Document`.
- `indexes`. See [Indexes](#indexes) section of this readme. Defaults to an
instance of `JamesMoss\Flywheel\Formatter\JSON`.


## Formats
Expand All @@ -252,12 +254,35 @@ The following formatter classes are available.
**Important** If you use the `YAML` or `Markdown` formatters when using the `--no-dev` flag in Composer you'll need
to manually add `mustangostang\spyc` to your `composer.json`. Flywheel tries to keep it's dependencies to a minimum.

If you write your own formatter it must implement `JamesMoss\Flywheel\Formatter\Format`.
If you write your own formatter it must implement `JamesMoss\Flywheel\Formatter\FormatInterface`.

## Indexes

To speed up the queries on some specific fields you can add indexes on these fields.
There are different types of index, each specialized on certain queries:

- `HashIndex` - supports only `==` and `!=` operators.

If a query cannot be executed only on indexes it will fallback to the default behaviour, which is opening all the files of the repository.

**Important** Indexes maintain their own copy of the data so you won't be able to manually edit the files while keeping the consistency of the database anymore.

```php
use JamesMoss\Flywheel\Index\HashIndex;

$config = new Config('/path/to/writable/directory', array(
'indexes' => {
'fieldname' => HashIndex::class
},
))
```

If you write your own index it must implement `JamesMoss\Flywheel\Index\IndexInterface`.

## Todo

- More caching around `Repository::findAll`.
- Indexing.
- More Indexes.
- HHVM support.
- Abstract the filesystem, something like Gaufrette or Symfony's Filesystem component?
- Events system.
Expand Down
5 changes: 3 additions & 2 deletions src/JamesMoss/Flywheel/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ public function getPath()
* Gets a specific option from the config
*
* @param string $name The name of the option to return.
* @param mixed $default The default value to use.
*
* @return mixed The value of the option if it exists or null if it doesnt.
*/
public function getOption($name)
public function getOption($name, $default = null)
{
return isset($this->options[$name]) ? $this->options[$name] : null;
return isset($this->options[$name]) ? $this->options[$name] : $default;
}

public function hasAPC()
Expand Down
16 changes: 12 additions & 4 deletions src/JamesMoss/Flywheel/Formatter/JSON.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,30 @@

namespace JamesMoss\Flywheel\Formatter;

defined('JSON_OBJECT_AS_ARRAY') or define('JSON_OBJECT_AS_ARRAY', 1);
defined('JSON_PRETTY_PRINT') or define('JSON_PRETTY_PRINT', 128);

class JSON implements FormatInterface
{
protected $jsonOptions;

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

public function getFileExtension()
{
return 'json';
}

public function encode(array $data)
{
$options = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : null;

return json_encode($data, $options);
return json_encode($data, $this->jsonOptions);
}

public function decode($data)
{
return json_decode($data);
return json_decode($data, $this->jsonOptions&JSON_OBJECT_AS_ARRAY);
}
}
104 changes: 104 additions & 0 deletions src/JamesMoss/Flywheel/Index/HashIndex.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

namespace JamesMoss\Flywheel\Index;

use JamesMoss\Flywheel\Formatter\JSON;
use JamesMoss\Flywheel\Index\StoredIndex;
use stdClass;

class HashIndex extends StoredIndex
{
protected static $operators = array(
'==', '!='
);

/**
* @inheritdoc
*/
public function __construct($field, $repository) {
$this->construct($field, $repository, new JSON(JSON_OBJECT_AS_ARRAY));
}

/**
* @inheritdoc
*/
public function isOperatorCompatible($operator)
{
return in_array($operator, self::$operators);
}

/**
* @inheritdoc
*/
protected function initData()
{
$this->data = array();
}

/**
* @inheritdoc
*/
protected function getEntries($value, $operator)
{
if (!isset($this->data[$value])) {
return array();
}
switch ($operator) {
case '==': return array_keys($this->data[$value]);
case '!=': return $this->idsExcept($value);
default: throw new \InvalidArgumentException('Incompatible operator `'.$operator.'`.');
}
}

/**
* Adds an entry in the index
*
* @param string $id
* @param string $value
*/
protected function addEntry($id, $value)
{
if (!isset($this->data[$value])) {
$this->data[$value] = array();
}
$this->data[$value][$id] = 1;
}

/**
* Removes an entry from the index
*
* @param string $id
* @param string $value
*/
protected function removeEntry($id, $value)
{
if (!isset($this->data[$value])) {
return;
}
unset($this->data[$value][$id]);
if (count($this->data[$value]) === 0) {
unset($this->data[$value]);
}
}

/**
* @inheritdoc
*/
protected function updateEntry($id, $new, $old)
{
if ($new !== null) {
$this->addEntry($id, $new);
}
if ($old !== null) {
$this->removeEntry($id, $old);
}
}

protected function idsExcept($value) {
$data = $this->data;
unset($data[$value]);
return array_keys(array_reduce($data, function($prev, $val) {
return array_merge($prev, $val);
}, array()));
}
}
51 changes: 51 additions & 0 deletions src/JamesMoss/Flywheel/Index/IndexInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace JamesMoss\Flywheel\Index;

interface IndexInterface
{
/**
* Constructor
*
* @param string $field the field to index.
* @param Repository $repository the repository of this index.
*/
public function __construct($field, $repository);

/**
* Checks if the given operator is compatible with this index.
*
* @param string $operator the operator to check.
*
* @return bool true if it is compatible.
*/
public function isOperatorCompatible($operator);

/**
* Get documents from the index.
*
* @param mixed $value the value we are looking for.
* @param string $operator the operator used for comparision.
*
* @return array<int,string> a list of documents ids.
*/
public function get($value, $operator);

/**
* Update a document in the index.
*
* @param string $id the id of this document.
* @param mixed $new the new value of this document for the indexed field.
* @param mixed $old the old value of this document for the indexed field.
*
* @return void
*/
public function update($id, $new, $old);

/**
* Regenerate the index from the repository's documents.
*
* @return void;
*/
public function regenerate();
}
Loading