<?php
/**
 * Horde YAML package
 *
 * This package is heavily inspired by the Spyc PHP YAML
 * implementation (http://spyc.sourceforge.net/), and portions are
 * copyright 2005-2006 Chris Wanstrath.
 *
 * @author   Chris Wanstrath <chris@ozmm.org>
 * @author   Chuck Hagenbuch <chuck@horde.org>
 * @author   Mike Naberezny <mike@maintainable.com>
 * @license  http://www.horde.org/licenses/bsd BSD
 * @category Horde
 * @package  Yaml
 */

/**
 * Horde YAML parser.
 *
 * This class can be used to read a YAML file and convert its contents
 * into a PHP array. The native PHP parser supports a limited
 * subsection of the YAML spec, but if the syck extension is present,
 * that will be used for parsing.
 *
 * @category Horde
 * @package  Yaml
 */
class Horde_Yaml
{
	/**
	 * Callback used for alternate YAML loader, typically exported
	 * by a faster PHP extension.  This function's first argument
	 * must accept a string with YAML content.
	 *
	 * @var callback
	 */
	public static $loadfunc = 'syck_load';

	/**
	 * Callback used for alternate YAML dumper, typically exported
	 * by a faster PHP extension.  This function's first argument
	 * must accept a mixed variable to be dumped.
	 *
	 * @var callback
	 */
	public static $dumpfunc = 'syck_dump';

	/**
	 * Whitelist of classes that can be instantiated automatically
	 * when loading YAML docs that include serialized PHP objects.
	 *
	 * @var array
	 */
	public static $allowedClasses = array('ArrayObject');

	/**
	 * Load a string containing YAML and parse it into a PHP array.
	 * Returns an empty array on failure.
	 *
	 * @param  string  $yaml   String containing YAML
	 * @return array           PHP array representation of YAML content
	 */
	public static function load($yaml)
	{
		if (!is_string($yaml) || !strlen($yaml)) {
			$msg = 'YAML to parse must be a string and cannot be empty.';
			throw new InvalidArgumentException($msg);
		}

		if (is_callable(self::$loadfunc)) {
			return call_user_func(self::$loadfunc, $yaml);
		}

		if (strpos($yaml, "\r") !== false) {
			$yaml = str_replace(array("\r\n", "\r"), array("\n", "\n"), $yaml);
		}
		$lines = explode("\n", $yaml);
		$loader = new Horde_Yaml_Loader;

		while (list(,$line) = each($lines)) {
			$loader->parse($line);
		}

		return $loader->toArray();
	}

	/**
	 * Load a file containing YAML and parse it into a PHP array.
	 *
	 * If the file cannot be opened, an exception is thrown.  If the
	 * file is read but parsing fails, an empty array is returned.
	 *
	 * @param  string  $filename     Filename to load
	 * @return array                 PHP array representation of YAML content
	 * @throws InvalidArgumentException  If $filename is invalid
	 * @throws Horde_Yaml_Exception  If the file cannot be opened.
	 */
	public static function loadFile($filename)
	{
		if (!is_string($filename) || !strlen($filename)) {
			$msg = 'Filename must be a string and cannot be empty';
			throw new InvalidArgumentException($msg);
		}

		$stream = @fopen($filename, 'rb');
		if (!$stream) {
			throw new Horde_Yaml_Exception('Failed to open file: ', error_get_last());
		}

		return self::loadStream($stream);
	}

	/**
	 * Load YAML from a PHP stream resource.
	 *
	 * @param  resource  $stream     PHP stream resource
	 * @return array                 PHP array representation of YAML content
	 */
	public static function loadStream($stream)
	{
		if (! is_resource($stream) || get_resource_type($stream) != 'stream') {
			throw new InvalidArgumentException('Stream must be a stream resource');
		}

		if (is_callable(self::$loadfunc)) {
			return call_user_func(self::$loadfunc, stream_get_contents($stream));
		}

		$loader = new Horde_Yaml_Loader;
		while (!feof($stream)) {
			$loader->parse(stream_get_line($stream, 100000, "\n"));
		}

		return $loader->toArray();
	}

	/**
	 * Dump a PHP array to YAML.
	 *
	 * The dump method, when supplied with an array, will do its best
	 * to convert the array into friendly YAML.
	 *
	 * @param  array|Traversable  $value     PHP array or traversable object
	 * @param  array            $options   Options to pass to dumper
	 * @return string                        YAML representation of $value
	 */
	public static function dump($value, $options = array())
	{
		if (is_callable(self::$dumpfunc)) {
			return call_user_func(self::$dumpfunc, $value);
		}

		$dumper = new Horde_Yaml_Dumper();
		return $dumper->dump($value, $options);
	}

}
