diff --git a/src/Factory.php b/src/Factory.php index 825f071..48263b4 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -25,6 +25,13 @@ class Factory */ public static $readers = null; + /** + * Plugin manager for loading writers + * + * @var null|WriterPluginManager + */ + public static $writers = null; + /** * Registered config file extensions. * key is extension, value is reader instance or plugin name @@ -38,6 +45,19 @@ class Factory 'yaml' => 'yaml', ); + /** + * Register config file extensions for writing + * key is extension, value is writer instance or plugin name + * + * @var array + */ + protected static $writerExtensions = array( + 'php' => 'php', + 'ini' => 'ini', + 'json' => 'json', + 'xml' => 'xml', + 'yaml' => 'yaml', + ); /** * Read a config from a file. @@ -107,10 +127,67 @@ public static function fromFiles(array $files, $returnConfigObject = false) return ($returnConfigObject) ? new Config($config) : $config; } + /** + * Writes a config to a file + * + * @param string $filename + * @param array|Config $config + * @return boolean TRUE on success | FALSE on failure + * @throws Exception\RuntimeException + * @throws Exception\InvalidArgumentException + */ + public static function toFile($filename, $config) + { + if ( + (is_object($config) && !($config instanceOf Config)) || + (!is_object($config) && !is_array($config)) + ) { + throw new Exception\InvalidArgumentException( + __METHOD__." \$config should be an array or instance of Zend\\Config\\Config" + ); + } + + $extension = substr(strrchr($filename, '.'), 1); + $directory = dirname($filename); + + if (!is_dir($directory)) { + throw new Exception\RuntimeException( + "Directory '{$directory}' does not exists!" + ); + } + + if (!is_writable($directory)) { + throw new Exception\RuntimeException( + "Cannot write in directory '{$directory}'" + ); + } + + if(!isset(self::$writerExtensions[$extension])) { + throw new Exception\RuntimeException( + "Unsupported config file extension: '.{$extension}' for writing." + ); + } + + $writer = self::$writerExtensions[$extension]; + if (($writer instanceOf Writer\AbstractWriter) === false) { + $writer = self::getWriterPluginManager()->get($writer); + self::$writerExtensions[$extension] = $writer; + } + + if (is_object($config)) { + $config = $config->toArray(); + } + + $content = $writer->processConfig($config); + + return (bool) (file_put_contents($filename, $content) !== false); + } + /** * Set reader plugin manager * * @param ReaderPluginManager $readers + * @return void */ public static function setReaderPluginManager(ReaderPluginManager $readers) { @@ -130,12 +207,38 @@ public static function getReaderPluginManager() return static::$readers; } + /** + * Set writer plugin manager + * + * @param WriterPluginManager $writers + * @return void + */ + public static function setWriterPluginManager(WriterPluginManager $writers) + { + self::$writers = $writers; + } + + /** + * Get the writer plugin manager + * + * @return WriterPluginManager + */ + public static function getWriterPluginManager() + { + if (static::$writers === null) { + static::$writers = new WriterPluginManager(); + } + + return static::$writers; + } + /** * Set config reader for file extension * * @param string $extension * @param string|Reader\ReaderInterface $reader * @throws Exception\InvalidArgumentException + * @return void */ public static function registerReader($extension, $reader) { @@ -152,4 +255,28 @@ public static function registerReader($extension, $reader) static::$extensions[$extension] = $reader; } + + /** + * Set config writer for file extension + * + * @param string $extension + * @param string|Writer\AbstractWriter $writer + * @throw Exception\InvalidArgumentException + * @return void + */ + public static function registerWriter($extension, $writer) + { + $extension = strtolower($extension); + + if (!is_string($writer) && !$writer instanceof Writer\AbstractWriter) { + throw new Exception\InvalidArgumentException(sprintf( + 'Writer should be plugin name, class name or ' . + 'instance of %s\Writer\AbstractWriter; received "%s"', + __NAMESPACE__, + (is_object($writer) ? get_class($writer) : gettype($writer)) + )); + } + + self::$writerExtensions[$extension] = $writer; + } } diff --git a/src/WriterPluginManager.php b/src/WriterPluginManager.php new file mode 100755 index 0000000..35717f5 --- /dev/null +++ b/src/WriterPluginManager.php @@ -0,0 +1,29 @@ + 'Zend\Config\Writer\PhpArray', + 'ini' => 'Zend\Config\Writer\Ini', + 'json' => 'Zend\Config\Writer\Json', + 'yaml' => 'Zend\Config\Writer\Yaml', + 'xml' => 'Zend\Config\Writer\Xml', + ); + + public function validatePlugin($plugin) + { + if ($plugin instanceOf Writer\AbstractWriter) { + return; + } + + $type = is_object($plugin) ? get_class($plugin) : gettype($plugin); + + throw new Exception\InvalidArgumentException( + "Plugin of type {$type} is invalid. Plugin must extend ". + __NAMESPACE__.'\Writer\AbstractWriter' + ); + } +} diff --git a/test/FactoryTest.php b/test/FactoryTest.php index b78abe2..ee70b8e 100644 --- a/test/FactoryTest.php +++ b/test/FactoryTest.php @@ -20,6 +20,28 @@ */ class FactoryTest extends \PHPUnit_Framework_TestCase { + protected $tmpFiles = array(); + + protected function getTestAssetFileName($ext) + { + if (empty($this->tmpfiles[$ext])) { + $this->tmpfiles[$ext] = tempnam(sys_get_temp_dir(), 'zend-config-writer').'.'.$ext; + } + return $this->tmpfiles[$ext]; + } + + public function tearDown() + { + foreach($this->tmpFiles as $file) { + if (file_exists($file)) { + if (!is_writable($file)) { + chmod($file, 0777); + } + @unlink($file); + } + } + } + public function testFromIni() { $config = Factory::fromFile(__DIR__ . '/TestAssets/Ini/include-base.ini'); @@ -125,7 +147,7 @@ public function testFactoryCanRegisterCustomReaderInstance() $this->assertEquals($configObject['one'], 1); } - public function testFactoryCanRegisterCustomReaderPlugn() + public function testFactoryCanRegisterCustomReaderPlugin() { $dummyReader = new Reader\TestAssets\DummyReader(); Factory::getReaderPluginManager()->setService('DummyReader', $dummyReader); @@ -138,5 +160,73 @@ public function testFactoryCanRegisterCustomReaderPlugn() $this->assertEquals($configObject['one'], 1); } + public function testFactoryToFileInvalidFileExtension() + { + $this->setExpectedException('RuntimeException'); + $result = Factory::toFile(__DIR__.'/TestAssets/bad.ext', array()); + } + + public function testFactoryToFileNoDirInHere() + { + $this->setExpectedException('RuntimeException'); + $result = Factory::toFile(__DIR__.'/TestAssets/NoDirInHere/nonExisiting/dummy.php', array()); + } + + public function testFactoryWriteToFile() + { + $config = array('test' => 'foo', 'bar' => array(0 => 'baz', 1 => 'foo')); + + $file = $this->getTestAssetFileName('php'); + $result = Factory::toFile($file, $config); + + // build string line by line as we are trailing-whitespace sensitive. + $expected = " 'foo',\n"; + $expected .= " 'bar' => \n"; + $expected .= " array (\n"; + $expected .= " 0 => 'baz',\n"; + $expected .= " 1 => 'foo',\n"; + $expected .= " ),\n"; + $expected .= ");\n"; + + $this->assertEquals(true, $result); + $this->assertEquals($expected, file_get_contents($file)); + } + + public function testFactoryToFileWrongConfig() + { + $this->setExpectedException('InvalidArgumentException'); + $result = Factory::toFile('test.ini', 'Im wrong'); + } + + public function testFactoryRegisterInvalidWriter() + { + $this->setExpectedException('InvalidArgumentException'); + Factory::registerWriter('dum', new Reader\TestAssets\DummyReader()); + } + + public function testFactoryCanRegisterCustomWriterInstance() + { + Factory::registerWriter('dum', new Writer\TestAssets\DummyWriter()); + + $file = $this->getTestAssetFileName('dum'); + $res = Factory::toFile($file, array('one' => 1)); + + $this->assertEquals($res, true); + } + + public function testFactoryCanRegisterCustomWriterPlugin() + { + $dummyWriter = new Writer\TestAssets\DummyWriter(); + Factory::getWriterPluginManager()->setService('DummyWriter', $dummyWriter); + + Factory::registerWriter('dum', 'DummyWriter'); + + $file = $this->getTestAssetFileName('dum'); + + $res = Factory::toFile($file, array('one' => 1)); + $this->assertEquals($res, true); + } } diff --git a/test/Writer/TestAssets/DummyWriter.php b/test/Writer/TestAssets/DummyWriter.php new file mode 100755 index 0000000..d5a989c --- /dev/null +++ b/test/Writer/TestAssets/DummyWriter.php @@ -0,0 +1,22 @@ +