<?php
class YaDisk
{
	const
		YD='https://webdav.yandex.ru/';

	protected
		/** @property resource $curl */
		$curl;

	/**
	 * @param string $token Токен, полученный при помощи YaAuth 
	 */
	public function __construct($token)
	{
		$this->token=$token;
	} 

	public function __destruct()
	{
		if($this->curl)
			curl_close($this->curl);
	}

	protected function GetCurl($opts=array())
	{
		if($this->curl)
			curl_close($this->curl);

		$this->curl=curl_init();

		curl_setopt_array($this->curl,$opts+array(
			CURLOPT_HEADER=>0,
			CURLOPT_CONNECTTIMEOUT=>2,
			CURLOPT_RETURNTRANSFER=>1,
			CURLOPT_SSL_VERIFYPEER=>false,
			CURLOPT_PORT=>443,
			CURLOPT_HTTP_VERSION=>CURL_HTTP_VERSION_1_1,
			CURLOPT_HTTPHEADER=>array('Accept: */*','Authorization: OAuth '.$this->token,'Depth: 1'),
		));
	}

	/**
	 * @param array $codes Удачные коды ответа
	 * @return string
	 * @throws EE
	 */
	public function GetResult($codes=array())
	{
		$response=curl_exec($this->curl);
		$code=curl_getinfo($this->curl,CURLINFO_HTTP_CODE);  
		$error=curl_error($this->curl);

		if($error)
			throw new EE($error,EE::UNIT);

		if(!in_array($code,$codes))
			throw new EE('Response code '.$code.' does not match successful codes ('.join(', ',$codes).')',EE::UNIT);

		return$response;
	}

	/**
	 * Создание каталога
	 * @param string $dirname Полное имя каталога относительно корня
	 */
	public function MkDir($dirname)
	{
		$this->GetCurl(array(
			CURLOPT_URL=>static::YD.$dirname,
			CURLOPT_CUSTOMREQUEST=>'MKCOL',
		));

		#405 код козвращается если папка уже есть на сервере
		return$this->GetResult(array(201,405));
	}

	/**
	 * Просмотр списка файлов и каталогов
	 * @param string $dirname Полное имя каталога относительно корня
	 * @return array
	 */
	public function Scandir($dirname='')
	{
		$this->GetCurl(array(
			CURLOPT_URL=>static::YD.$dirname,
			CURLOPT_CUSTOMREQUEST=>'PROPFIND',
		));

		$response=$this->GetResult(array(207));
		$xml=simplexml_load_string($response,'SimpleXMLElement',0,'d',true);
		$list=array(
			'dirs'=>array(),
			'files'=>array()
		);

		$dirlen=strlen($dirname);
		foreach($xml as $item)
		{
			$name=trim($item->href,'/');

			if($name)
			{
				$type=isset($item->propstat->prop->resourcetype->collection) ? 'dirs' : 'files';
				$list[$type][]=ltrim(substr(urldecode($name),$dirlen),'/');
			}
		}

		return$list; 
	}

	/**
	 * Загрузка файлов на Яндекс.Диск
	 * @param string $locsrc Файл на локальном сервере
	 * @param string $servdest Полное имя удаленного файла относительно корня
	 * @throws EE
	 * @return string
	 */
	public function Upload($locsrc,$servdest)
	{
		if(!is_file($locsrc) or !is_readable($locsrc))
			throw new EE('File '.$locsrc.' inaccessible.',EE::ENV);

		$fh=fopen($locsrc,'rb');

		$this->GetCurl(array(
			CURLOPT_URL=>static::YD.$servdest,
			CURLOPT_PUT=>1,
			CURLOPT_INFILE=>$fh,
			CURLOPT_INFILESIZE=>filesize($locsrc),
		));

		try
		{
			$result=$this->GetResult(array(200,201,204));
		}
		catch(EE$E)#ToDo! PHP 5.5 replace with finally
		{
			fclose($fh);
			throw$E;
		}

		fclose($fh);
		return$result;
	}

	/**
	 * Скачивание файла с Яндекс.Диска
	 * @param string $servsrc Полное имя удаленного файла относительно коря
	 * @param string $locdest Полное имя будущего файла на локальном сервере
	 * @throws EE
	 * @return string
	 */
	function Download($servsrc,$locdest)
	{
		if(is_file($locdest) or !is_writeable(dirname($locdest)))
			throw new EE('File '.$locdest.' uncreatable.',EE::ENV);

		$fh=fopen($locdest,'w');

		$this->GetCurl(array(
			CURLOPT_URL=>static::YD.$servsrc,
			CURLOPT_HTTPGET=>1,
			CURLOPT_FILE=>$fh,
		));

		try
		{
			$result=$this->GetResult(array(200));
		}
		catch(EE$E)#ToDo! PHP 5.5 replace with finally
		{
			fclose($fh);
			throw$E;
		}

		fclose($fh);
		return$result; 
	}

	/**
	 * Удаление файла или каталога
	 * @param string $path Полный путь к удаляемому файлу
	 */
	public function Unlink($path)
	{
		$this->GetCurl(array(
			CURLOPT_URL=>static::YD.$path,
			CURLOPT_CUSTOMREQUEST=>'DELETE',
		));

		#405 код козвращается если папка уже есть на сервере
		return$this->GetResult(array(200));
	}  
}
