Skip to content

Commit

Permalink
Merge pull request #42 from chrismichaels84/develop
Browse files Browse the repository at this point in the history
Merge Develop into Master for v0.8.8
  • Loading branch information
Michael Wilson committed Dec 30, 2015
2 parents 88b8c97 + 51cacfe commit f7af16a
Show file tree
Hide file tree
Showing 24 changed files with 646 additions and 124 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
# Changelog
All Notable changes to `Manager` will be documented in this file

# v0.8.7 - NEXT
# v0.8.9 - NEXT

# v0.8.8 - 12-29-2015
- Auto namespaces FileLoader data under the file's name
- Allows for custom namespacing as [fileObject, 'namespace']
- Allows for loading files using just a valid path
- Reproduces Arrayzy API inside CollectionsTrait
- Deals with collisions between ChainsNestedItemsTrait and CollectionsTrait
- Moves and rename $_items into ManagesItemsTrait

# v0.8.7 - 12-2-2015
- Moves helper methods to a static `Helpers` class
- Converts all test methods to `snake_case` not `camelCase`

Expand Down
38 changes: 32 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ They are not documented here, but see other readmes for:
* Fallbacks, preparing objects, and more.
* Use Manager as a configuration bank, complete with defaults.
* Load configuration files (php, yaml, json, xml, and custom)
* Collections for extra array-helper methods (based on [Arrayzy](https://github.com/bocharsky-bw/Arrayzy))
* [Collections](collections.md) for extra array-helper methods (based on [Arrayzy](https://github.com/bocharsky-bw/Arrayzy))

## Install
Via Composer
Expand Down Expand Up @@ -225,15 +225,41 @@ In this case, `walk()` is part of the collection and will apply the callback to
You may also disable this by `$manager->useCollections = false`

### Loading Files
Manager also gives you the ability to load file data into the Manager. A good use case for this is loading configuration data out of different configuration files.
Manager also gives you the ability to load file data into the Manager.
A good use case for this is loading configuration data out of different configuration files.

1. Use `$manager->loadFiles($files)` to load a group of files. The `$files` argument can be either a `\FileBag` object or an array of `\SplFileInfo` objects. Most likely you would use an array, like the return result of [Symfony's Finder Component](https://github.com/symfony/Finder), which can locate files in a directory tree and return them as `\SplFileInfo` objects.
2. If you have created your own type of config file, you can also create a decoder for those types of files. All you need to do is follow the `\DecoderInterface` interface and return a php array of data through the `decode()` method. Then, once you have a decoder, you can add the decoder to the file loader with `$manager->addDecoder($decoder);` Once the decoder is added, you can then load your custom config files to the Manager, as in the previous step.
1. `use LoadsFilesTrait`
2. Use `$manager->loadFiles($files)` to load a group of files.

The `$files` argument can be a `FileBag` object or an array of:
1. `\SplFileInfo` objects
2. Valid paths (`__DIR__.'/some/path/here.json'`)

For more powerful uses (like loading entire directories or advanced filesystem searches), you may also us [Symfony's Finder Component](https://github.com/symfony/Finder).
```php
$finder = new Finder();
$finder->files()->in(__DIR__);
$manager->loadFiles($finder);
```

NOTE: Currently Manager supports Yaml, Json and PHP file formats out of the box. To use the Yaml decoder, you must require the Symfony Yaml Component in your composer.json file.
The data will be added to manager under the filename. So, if you load `config.json` you could `$manager->get('config.item')`;
It is possible to set a custom namespace for each file:
```php
$manager->loadFiles([
__DIR__.'/path/to/filename.json', // auto namespaces under filename
[new \SplFileInfo('path/to/file.yaml'), 'namespace'], // will namespace like ->get('namespace.item')
[__DIR__.'/path/to/another.php, 'another'] // will namespace like ->get('another.item')
]);
```

#### Adding a Decoder
If you have special files not covered by the default decoders available in Manager, you can also create your own and add it to the Manager prior to decoding the files with the `$manager->loadFiles()` method. For an example custom decoder, have a look at the `\CustomXmlDecoder` class in the `/Decoders` directory. Once you've created your custom decoder, you can add it with the `$manager->addDecoder()` method. Again, you must add the decoder prior to loading any file data.
Manager supports Yaml, Json and PHP file formats out of the box. To use the Yaml decoder, you must require the Symfony Yaml Component in your composer.json file.

If you have special files not covered by the decoders available in Manager, you can also create your own and add it to the Manager prior to decoding the files.
Simply implement the `Contracts\DecoderInterface` and return an array of data (not namespaced).
For an example custom decoder, have a look at the `\CustomXmlDecoder` class in the `/Decoders` directory.

Once you've created your custom decoder, you can add it with the `$manager->addDecoder()` method *before* loading any file data.

### Managing Dependencies
Please see [ioc.md](ioc.md) for details.
Expand Down
104 changes: 104 additions & 0 deletions collections.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Data Manager and Collections
On top of being a powerful data-manager, `Manager` can be used as an array collection.
This means it provides several helper methods to work with the data.

The Collections API is based on (and uses) [Arrayzy](https://github.com/bocharsky-bw/Arrayzy), so be sure
to look at that package for a complete list of available methods.

Also note that some conflicts exist (such as `contains`) in these cases, Manager's method is used.
There should be little or no difference in the behavior.

## Setup and Usage
Simply include the `Michaels\Manager\Collections` in your class.

**NOTE THAT** `ManagesIocTrait` depends on ManagesItemsTrait.
If you to use `ManagesIocTrait` without `ManagesItemsTrait`, you will get all sorts of errors.

### Returning Collections
Once you have that included, it will (by default) return a collection every time you ask for an array.
That means
```php
$manager = new Manager([
'one' => [
'two' => [
'three' => 'three',
'four' => 'four',
'five' => 'five'
]
]
]);

$value = $manager->get('one.two');
```

Will return an instance that allows you to `push`, `pop` and all sorts of things.
```php
$value->shift('six');
```
And so on. Again, look at [Arrayzy](https://github.com/bocharsky-bw/Arrayzy) for details.

Of course, if you ask for an item that can't be used as a collection, you get the item itself.

Lastly, you may enable or disable whether to return collections by changing the `$useCollections` property on the class.

### Using Dot Notation
You can also use the Collection API via dot notation
```php
$manager = new Collection(['one' => ['two' => ['a', 'b', 'c']]]);
$manager->unshift('one.two', 'y', 'z');

// ['y', 'z', 'a', 'b', 'c']
```

In these cases, the only difference between Manager's api and Arrayzy's api is that the FIRST argument
is the alias you want to mess with.

When doing this, these methods return data. By default they will return arrays.
You may change this. You can return arrays, collections, or modify the data in manager itself by adding a third argument.

```php
$manager->unshift('one.two', 'y', Collection::$RETURN_COLLECTION);
```
This will do exactly the same as above, but will give you back a collection instead of array.

At present the following flags exist:
1. `static::$RETURN_ARRAY` by default
2. `static::$RETURN_COLLECTION` gives you an Arrayzy Collection
3. `static::$MODIFY_MANIFEST` will modify the data inside manager and return manager ($this) to you.

An example of the last:
```php
$manager = new Collection(['one' => ['two' => ['a', 'b', 'c']]]);
$manager->add('three', 'three-value');

$actual = $manager->walk('one.two', function(&$value, $key) {
$value = "$value-new";
}, Collection::$MODIFY_MANIFEST);

// $actual is the manager itself
// $manager->get('three') === 'three-value'
// $manager->get('one.two') === ['a-new', 'b-new', 'c-new'];
```

## Using with `ChainsNestedItemsTrait`
It is possible to combine these two traits, though it isn't recommended. With so many changable method names, you are bound
to run into some name collisions and there is no telling how it will be handled. This feature is not officially supported, but
I did make it possible and am open to suggestions (PRs) for improvement.

In order to do this, you MUST setup your class like
```php
class MyCoolCollection implements ManagesItemsInterface
{
use ManagesItemsTrait, ChainsNestedItemsTrait, CollectionTrait {
CollectionTrait::__call insteadof ChainsNestedItemsTrait;
}

public function __construct(array $items = null)
{
$this->initManager($items);
}
}
```

## Contributing
Any feedback here would be appreciated. Open an issue or PR. See [contributing](contributing.md) for more info.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
},
"require-dev": {
"phpunit/phpunit": "4.*",
"satooshi/php-coveralls": "dev-master"
"satooshi/php-coveralls": "1.*"
},
"suggest": {
"symfony/yaml": "If you are using the YAML decoder. ~2.7",
Expand Down
36 changes: 27 additions & 9 deletions src/Bags/FileBag.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,40 @@ protected function initialize(array $splFileInfoObjects = [])
{
if (!empty($splFileInfoObjects)) {
foreach ($splFileInfoObjects as $object) {
if ($this->isSplFileInfoObject($object)) {
array_push($this->fileObjects, $object);
} else {
throw new BadFileInfoObjectException('The input array does not hold proper SplFileInfo objects.');
}
array_push($this->fileObjects, $this->createSplFileInfoObject($object));
}
}
}

/**
* Check for an \SplFileInfo object
* @param $object
* Check for an \SplFileInfo object or a custom namespaces object
* @param $entity
* @return bool
*/
protected function isSplFileInfoObject($object)
protected function createSplFileInfoObject($entity)
{
return ($object instanceof \SplFileInfo);
// This is already a valid object
if ($entity instanceof \SplFileInfo) {
return $entity;
}

// This is a valid path
if (is_string($entity) && file_exists($entity)) {
return new \SplFileInfo($entity);
}

// This is an array with a valid object
$isArray = is_array($entity);
if ($isArray && $entity[0] instanceof \SplFileInfo) {
return $entity;
}

// This is an array with a valid path
if ($isArray && is_string($entity[0]) && file_exists($entity[0])) {
return [new \SplFileInfo($entity[0]), $entity[1]];
}

// We've run out of options
throw new BadFileInfoObjectException('The input array does not hold proper SplFileInfo objects.');
}
}
50 changes: 49 additions & 1 deletion src/FileLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,13 @@ public function decodeFileBagData(FileBag $fileBag)
}

foreach ($files as $file) {
list($namespace, $file) = $this->getFilesNamespace($file);

// Decode the actual file and save data
$fileData = $this->decodeFile($file);
if ($fileData) {
foreach ($fileData as $k => $v) {
$decodedData[$k] = $v;
$decodedData[$namespace][$k] = $v;
}
}
}
Expand Down Expand Up @@ -220,4 +223,49 @@ public function getMimeTypes()
{
return $this->supportedMimeTypes;
}

/**
* Cleans up a file name for use as a namespace
* @param $ns
* @return mixed
*/
public function sanitizeNamespace($ns)
{
// dots to underscores
$ns = str_replace(".", "_", $ns);

// spaces to underscores
$ns = str_replace(" ", "_", $ns);

// dashes to underscores
$ns = str_replace("-", "_", $ns);

// Alpha numeric
$ns = preg_replace("/[^A-Za-z0-9\.\_\- ]/", '', $ns);

return $ns;
}

/**
* Gets or creates the file's namespace
* @param $file
* @return array with the namespace and fileobject
*/
protected function getFilesNamespace($file)
{
$namespace = null;

if (is_array($file)) {
// We are using a custom namespace
$namespace = $this->sanitizeNamespace($file[1]);
$file = $file[0];
return [$namespace, $file];

} else {
// We are namespacing using the file's name
$filename = rtrim($file->getBasename(), '.' . $file->getExtension());
$namespace = $this->sanitizeNamespace($filename);
return [$namespace, $file];
}
}
}
18 changes: 14 additions & 4 deletions src/Traits/ChainsNestedItemsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,7 @@ public function __get($name)
*/
public function __call($name, $arguments)
{
$dot = ($this->currentLevel === false) ? '' : '.';
$this->currentLevel .= $dot . $name;

return $this;
return $this->addToChain($name);
}

/**
Expand Down Expand Up @@ -91,4 +88,17 @@ protected function buildPrefix()
return $prefix . ".";
}
}

/**
* Adds to the chain
* @param $name
* @return $this
*/
protected function addToChain($name)
{
$dot = ($this->currentLevel === false) ? '' : '.';
$this->currentLevel .= $dot . $name;

return $this;
}
}
Loading

0 comments on commit f7af16a

Please sign in to comment.