├── example ├── image.jpg ├── client_oauth.php ├── manual_request.php ├── disk.php └── backup_dir_to_ydisk.php ├── .editorconfig ├── .gitattributes ├── src ├── Client │ ├── Exception │ │ ├── ServiceException.php │ │ ├── ForbiddenException.php │ │ ├── UnsupportedException.php │ │ ├── NotFoundException.php │ │ └── UnauthorizedException.php │ ├── Container │ │ ├── Collection.php │ │ └── ContainerTrait.php │ ├── Stream │ │ ├── Factory.php │ │ └── Progress.php │ ├── SimpleKey.php │ ├── OAuth.php │ └── HttpClient.php ├── Disk │ ├── Exception │ │ ├── AlreadyExistsException.php │ │ └── OutOfSpaceException.php │ ├── Filter │ │ ├── TypeTrait.php │ │ ├── RelativePathTrait.php │ │ ├── PreviewTrait.php │ │ └── MediaTypeTrait.php │ ├── Resource │ │ ├── Collection.php │ │ ├── Removed.php │ │ ├── Opened.php │ │ └── Closed.php │ ├── AbstractResource.php │ ├── Operation.php │ └── FilterTrait.php ├── AbstractClient.php └── Disk.php ├── .gitignore ├── LICENSE.md ├── composer.json └── README.md /example/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack-theripper/yandex/HEAD/example/image.jpg -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | end_of_line = lf 7 | insert_final_newline = false 8 | -------------------------------------------------------------------------------- /example/client_oauth.php: -------------------------------------------------------------------------------- 1 | send($request); 18 | 19 | var_dump($response->getBody()->getContents()); 20 | 21 | // Но и этот факт можно использовать, например как получить превью закрытого ресурса 22 | $resource = $disk->getResource('disk:/O2cXW1AEVWI222.jpg') 23 | ->setPreview('100x250'); 24 | 25 | $request = new Request($resource->preview, 'GET'); 26 | $response = $disk->send($request); 27 | 28 | header('Content-type: image/jpeg'); 29 | 30 | echo $response->getBody(); 31 | 32 | -------------------------------------------------------------------------------- /src/Client/Exception/NotFoundException.php: -------------------------------------------------------------------------------- 1 | isModified = true; 39 | $this->parameters['type'] = $type; 40 | 41 | return $this; 42 | } 43 | 44 | /** 45 | * Получает установленное значение. 46 | * 47 | * @return string 48 | */ 49 | public function getType() 50 | { 51 | if (isset($this->parameters['type'])) 52 | { 53 | return $this->parameters['type']; 54 | } 55 | 56 | return null; 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /src/Disk/Filter/RelativePathTrait.php: -------------------------------------------------------------------------------- 1 | isModified = true; 38 | $this->parameters['path'] = '/'.ltrim($path, ' /'); 39 | 40 | return $this; 41 | } 42 | 43 | /** 44 | * Получает установленное значение. 45 | * 46 | * @return string 47 | */ 48 | public function getRelativePath() 49 | { 50 | if (isset($this->parameters['path'])) 51 | { 52 | return $this->parameters['path']; 53 | } 54 | 55 | return null; 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /src/Client/Container/Collection.php: -------------------------------------------------------------------------------- 1 | setContents($data); 33 | } 34 | 35 | /** 36 | * Получает первый элемент в списке 37 | * 38 | * @return mixed 39 | */ 40 | public function getFirst() 41 | { 42 | $elements = $this->toArray(); 43 | 44 | return reset($elements); 45 | } 46 | 47 | /** 48 | * Получает последний элемент в списке 49 | * 50 | * @return \Arhitector\Yandex\Disk\AbstractResource 51 | */ 52 | public function getLast() 53 | { 54 | $elements = $this->toArray(); 55 | 56 | return end($elements); 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /src/Client/Stream/Factory.php: -------------------------------------------------------------------------------- 1 | write((string) $body); 47 | } 48 | 49 | $body = $stream; 50 | } 51 | } 52 | 53 | $body->rewind(); 54 | 55 | return $body; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Disk/Resource/Collection.php: -------------------------------------------------------------------------------- 1 | closure = $data_closure; 45 | } 46 | 47 | /** 48 | * Получает информацию 49 | * 50 | * @return array 51 | */ 52 | public function toArray(array $allowed = null) 53 | { 54 | if ( ! parent::toArray() || $this->isModified()) 55 | { 56 | $this->setContents(call_user_func($this->closure, $this->getParameters($this->parametersAllowed))); 57 | $this->isModified = false; 58 | } 59 | 60 | return parent::toArray(); 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /src/Client/SimpleKey.php: -------------------------------------------------------------------------------- 1 | setAccessKey($accessKey); 46 | } 47 | } 48 | 49 | /** 50 | * Устанавливает ключ API. 51 | * 52 | * @param string $accessKey ключ API. 53 | * 54 | * @return $this 55 | * @throws \InvalidArgumentException 56 | */ 57 | public function setAccessKey($accessKey) 58 | { 59 | if ( ! is_string($accessKey)) 60 | { 61 | throw new \InvalidArgumentException('Ключ доступа должен быть строкового типа.'); 62 | } 63 | 64 | $this->accessKey = $accessKey; 65 | 66 | return $this; 67 | } 68 | 69 | /** 70 | * Получает установленный ключ API. 71 | * 72 | * @return string 73 | */ 74 | public function getAccessKey() 75 | { 76 | return (string) $this->accessKey; 77 | } 78 | 79 | /** 80 | * Провести аунтификацию в соостветствии с типом сервиса 81 | * 82 | * @param RequestInterface $request 83 | * 84 | * @return RequestInterface 85 | */ 86 | protected function authentication(RequestInterface $request) 87 | { 88 | $uri = $request->getUri(); 89 | $key = http_build_query(['key' => $this->getAccessKey()], '', '&'); 90 | 91 | if (strlen($uri->getQuery()) > 0) 92 | { 93 | $key = '&'.$key; 94 | } 95 | 96 | return $request->withUri($uri->withQuery($uri->getQuery().$key)); 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /src/Disk/Filter/PreviewTrait.php: -------------------------------------------------------------------------------- 1 | isModified = true; 34 | $this->parameters['preview_crop'] = (bool) $crop; 35 | 36 | return $this; 37 | } 38 | 39 | /** 40 | * Размер уменьшенного превью файла 41 | * 42 | * @param mixed $preview S, M, L, XL, XXL, XXXL, <ширина>, <ширина>x, x<высота>, <ширина>x<высота> 43 | * 44 | * @return $this 45 | * @throws \UnexpectedValueException 46 | */ 47 | public function setPreview($preview) 48 | { 49 | if (is_scalar($preview)) 50 | { 51 | $preview = strtoupper($preview); 52 | $previewNum = str_replace('X', '', $preview, $replaces); 53 | 54 | if (in_array($preview, ['S', 'M', 'L', 'XL', 'XXL', 'XXXL']) || (is_numeric($previewNum) && $replaces < 2)) 55 | { 56 | if (is_numeric($previewNum)) 57 | { 58 | $preview = strtolower($preview); 59 | } 60 | 61 | $this->isModified = true; 62 | $this->parameters['preview_size'] = $preview; 63 | 64 | return $this; 65 | } 66 | } 67 | 68 | throw new \UnexpectedValueException('Допустимые значения размера превью - S, M, L, XL, XXL, XXXL, <ширина>, <ширина>x, x<высота>, <ширина>x<высота>'); 69 | } 70 | 71 | /** 72 | * Получает установленное значение "setPreview". 73 | * 74 | * @return string 75 | */ 76 | public function getPreview() 77 | { 78 | if (isset($this->parameters['preview_size'])) 79 | { 80 | return $this->parameters['preview_size']; 81 | } 82 | 83 | return null; 84 | } 85 | 86 | /** 87 | * Получает установленное значение "setPreviewCrop". 88 | * 89 | * @return string 90 | */ 91 | public function getPreviewCrop() 92 | { 93 | if (isset($this->parameters['preview_crop'])) 94 | { 95 | return $this->parameters['preview_crop']; 96 | } 97 | 98 | return null; 99 | } 100 | 101 | } -------------------------------------------------------------------------------- /src/Disk/AbstractResource.php: -------------------------------------------------------------------------------- 1 | toArray()) 63 | { 64 | if ($index === null) 65 | { 66 | return true; 67 | } 68 | 69 | return $this->hasProperty($index); 70 | } 71 | } 72 | catch (\Exception $exc) 73 | { 74 | 75 | } 76 | 77 | return false; 78 | } 79 | 80 | /** 81 | * Проверяет, является ли ресурс файлом 82 | * 83 | * @return bool 84 | */ 85 | public function isFile() 86 | { 87 | return $this->get('type', false) === 'file'; 88 | } 89 | 90 | /** 91 | * Проверяет, является ли ресурс папкой 92 | * 93 | * @return bool 94 | */ 95 | public function isDir() 96 | { 97 | return $this->get('type', false) === 'dir'; 98 | } 99 | 100 | /** 101 | * Проверяет, этот ресурс с открытым доступом или нет 102 | * 103 | * @return boolean 104 | */ 105 | public function isPublish() 106 | { 107 | return $this->has('public_key'); 108 | } 109 | 110 | /** 111 | * Получить путь к ресурсу 112 | * 113 | * @return string 114 | */ 115 | public function getPath() 116 | { 117 | return $this->path; 118 | } 119 | 120 | } -------------------------------------------------------------------------------- /src/Disk/Operation.php: -------------------------------------------------------------------------------- 1 | uri = $uri; 71 | $this->parent = $disk; 72 | $this->identifier = $identifier; 73 | } 74 | 75 | /** 76 | * Текстовый статус операции. 77 | * 78 | * @return string|null NULL если не удалось получить статус. 79 | */ 80 | public function getStatus() 81 | { 82 | $response = $this->parent->send(new Request($this->uri->withPath($this->uri->getPath() . 'operations/' 83 | . $this->getIdentifier()), 'GET')); 84 | 85 | if ($response->getStatusCode() == 200) { 86 | $response = json_decode($response->getBody(), true); 87 | 88 | if (isset($response['status'])) { 89 | return $response['status']; 90 | } 91 | } 92 | 93 | return null; 94 | } 95 | 96 | /** 97 | * Получает используемый идентификатор. 98 | * 99 | * @return string 100 | */ 101 | public function getIdentifier() 102 | { 103 | return $this->identifier; 104 | } 105 | 106 | /** 107 | * Проверяет успешна ли операция. 108 | * 109 | * @return bool 110 | */ 111 | public function isSuccess() 112 | { 113 | return $this->getStatus() == self::SUCCESS; 114 | } 115 | 116 | /** 117 | * Если операция завершилась неудачей. 118 | * 119 | * @return bool 120 | */ 121 | public function isFailure() 122 | { 123 | return $this->getStatus() != 'success'; 124 | } 125 | 126 | /** 127 | * Операция в процессе выполнения. 128 | * 129 | * @return bool 130 | */ 131 | public function isPending() 132 | { 133 | return $this->getStatus() == self::PENDING; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Client/Stream/Progress.php: -------------------------------------------------------------------------------- 1 | totalSize = $this->getSize(); 51 | } 52 | 53 | /** 54 | * Read data from the stream. 55 | * 56 | * @param int $length Read up to $length bytes from the object and return 57 | * them. Fewer than $length bytes may be returned if underlying stream 58 | * call returns fewer bytes. 59 | * 60 | * @return string Returns the data read from the stream, or an empty string 61 | * if no bytes are available. 62 | * @throws \RuntimeException if an error occurs. 63 | */ 64 | public function read($length): string 65 | { 66 | $this->readSize += $length; 67 | $percent = round(100 / $this->totalSize * $this->readSize, 2); 68 | $this->emit('progress', min(100.0, $percent)); 69 | 70 | return parent::read($length); 71 | } 72 | 73 | /** 74 | * Returns the remaining contents in a string 75 | * 76 | * @return string 77 | * @throws \RuntimeException if unable to read or an error occurs while 78 | * reading. 79 | */ 80 | public function getContents(): string 81 | { 82 | $this->readSize = $this->totalSize; 83 | $this->emit('progress', 100.0); 84 | 85 | return parent::getContents(); 86 | } 87 | 88 | /** 89 | * Seek to a position in the stream. 90 | * 91 | * @param int $offset Stream offset 92 | * @param int $whence Specifies how the cursor position will be calculated 93 | * based on the seek offset. Valid values are identical to the built-in 94 | * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to 95 | * offset bytes SEEK_CUR: Set position to current location plus offset 96 | * SEEK_END: Set position to end-of-stream plus offset. 97 | * 98 | * @return void 99 | * @throws \RuntimeException on failure. 100 | */ 101 | public function seek($offset, $whence = SEEK_SET): void 102 | { 103 | try { 104 | parent::seek($offset, $whence); // <-- catch 105 | $this->readSize = $offset; 106 | } catch (UnseekableStreamException $exception) { 107 | 108 | } 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/Disk/FilterTrait.php: -------------------------------------------------------------------------------- 1 | isModified = true; 54 | $this->parameters['limit'] = (int) $limit; 55 | 56 | if ($offset !== null) 57 | { 58 | $this->setOffset($offset); 59 | } 60 | 61 | return $this; 62 | } 63 | 64 | /** 65 | * Количество вложенных ресурсов с начала списка, которые следует опустить в ответе 66 | * 67 | * @param integer $offset 68 | * 69 | * @return $this 70 | */ 71 | public function setOffset($offset) 72 | { 73 | if (filter_var($offset, FILTER_VALIDATE_INT) === false) 74 | { 75 | throw new \InvalidArgumentException('Параметр "offset" должен быть целым числом.'); 76 | } 77 | 78 | $this->isModified = true; 79 | $this->parameters['offset'] = (int) $offset; 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * Атрибут, по которому сортируется список ресурсов, вложенных в папку. 86 | * 87 | * @param string $sort 88 | * @param boolean $inverse TRUE чтобы сортировать в обратном порядке 89 | * 90 | * @return $this 91 | * @throws \UnexpectedValueException 92 | */ 93 | public function setSort($sort, $inverse = false) 94 | { 95 | $sort = (string) $sort; 96 | 97 | if ( ! in_array($sort, ['name', 'path', 'created', 'modified', 'size', 'deleted'])) 98 | { 99 | throw new \UnexpectedValueException('Допустимые значения сортировки - name, path, created, modified, size'); 100 | } 101 | 102 | if ($inverse) 103 | { 104 | $sort = '-'.$sort; 105 | } 106 | 107 | $this->isModified = true; 108 | $this->parameters['sort'] = $sort; 109 | 110 | return $this; 111 | } 112 | 113 | /** 114 | * Возвращает все параметры 115 | * 116 | * @return array 117 | */ 118 | public function getParameters(array $allowed = null) 119 | { 120 | if ($allowed !== null) 121 | { 122 | return array_intersect_key($this->parameters, array_flip($allowed)); 123 | } 124 | 125 | return $this->parameters; 126 | } 127 | 128 | /** 129 | * Контейнер изменил состояние 130 | * 131 | * @return bool 132 | */ 133 | protected function isModified() 134 | { 135 | return (bool) $this->isModified; 136 | } 137 | 138 | } -------------------------------------------------------------------------------- /src/Disk/Filter/MediaTypeTrait.php: -------------------------------------------------------------------------------- 1 | getMediaTypes())) { 135 | throw new \UnexpectedValueException( 136 | 'Неверный тип файла, допустимые значения: "'.implode('", "', $this->getMediaTypes()).'".' 137 | ); 138 | } 139 | 140 | $this->isModified = true; 141 | $this->parameters['media_type'] = $media_type; 142 | 143 | return $this; 144 | } 145 | 146 | /** 147 | * Получает установленное значение. 148 | * 149 | * @return string|null 150 | */ 151 | public function getMediaType() 152 | { 153 | return $this->parameters['media_type'] ?? null; 154 | } 155 | 156 | /** 157 | * Все возможные типы файлов. 158 | * 159 | * @return string[] 160 | */ 161 | public function getMediaTypes() 162 | { 163 | return $this->mediaTypes; 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /example/disk.php: -------------------------------------------------------------------------------- 1 | setAccessToken(''); 15 | 16 | /** 17 | * Список всех файлов на диске, метод getResources([int limit = 20, [int offset = 0]]. 18 | * Возвращает объект "Arhitector\Yandex\Disk\Resource\Collection" 19 | */ 20 | /*$resources = $disk->getResources() 21 | ->setLimit(10) // количество файлов, getResources может принять "limit" первым параметром. 22 | ->setOffset(0) // смещениеб пуеКуыщгксуы vj;tn ghbyznm "offset" вторым параметром. 23 | ->setMediaType('image') // мультимедиа тип файлов, все типы $resources->getMediaTypes() 24 | ->setPreview('100x250') // размер превью изображений 25 | ->setPreviewCrop(true) // обрезать превью согласно размеру 26 | ->setSort('name', true) // способ сортировки, второй параметр TRUE означает "в обратном порядке" 27 | ;*/ 28 | 29 | //$first = $resources->getFirst(); // Arhitector\Yandex\Disk\Resource\Closed 30 | //$last = $resources->getLast(); // Arhitector\Yandex\Disk\Resource\Closed 31 | 32 | //var_dump($first->toArray(), $last->toArray(), $resources->toArray()); 33 | 34 | /** 35 | * Работа с одним ресурсом, метод "getResource" 36 | * первым параметром принимает путь к ресурсу - папке или файлу. 37 | * вторым и третим принимает "limit" и "offset", если ресурс - это папка. 38 | */ 39 | //$resource = $disk->getResource('app:/picture.jpg'); 40 | 41 | //$has = $resource->has(); // проверить есть ли ресурс на диске. 42 | 43 | /*if ( ! $has) 44 | { 45 | 46 | // загружает локальный файл на диск. второй параметр отвечает за перезапись файла, если такой есть на диске. 47 | // загружает удаленный файл на диск, передайте ссылку http на удаленный файл. 48 | $resource->upload(__DIR__.'/image.jpg'); 49 | }*/ 50 | 51 | //$has_moved = $resource->move('image2.jpg'); // переместить, boolean 52 | //$result = $resource->upload('http://<..>file.zip'); 53 | //var_dump($result->getStatus(), $result->getIdentifier()); 54 | 55 | //$resource->custom_index_1 = 'value'; // добавить метаинформацию в "custom_properties" 56 | //$resource->set('custom_index_2', 'value'); // добавить метаинформацию в "custom_properties" 57 | //$resource['custom_index_3'] = 'value'; // добавить метаинформацию в "custom_properties" 58 | //unset($resource['custom_index_3']); // удалить метаинформацию, или передать NULL в "set" чтобы удалить 59 | 60 | //echo 'View'; 61 | 62 | //$resource->download(__DIR__.'/image_downloaded.jpg'); // скачать файл с диска 63 | 64 | //var_dump($has, $resource->getProperties(), $resource->toObject()); 65 | //$resource = $disk->getPublishResource('https://yadi.sk/d/WSS6bK_ksQ5ck'); 66 | //var_dump($resource->items->get(0)->getLink(), $resource->toArray()); 67 | //var_dump($resource->download(__DIR__.'/down.zip', true)); 68 | //var_dump($resource->items->getFirst()->toArray(), $resource->toArray()); 69 | 70 | //$resources = $disk->getTrashResources(5); 71 | //$resources = $disk->getTrashResource('trash:/image2.jpg'); 72 | //var_dump($resources->toArray()); 73 | 74 | /* 75 | $disk->addListener('operation', function ($event, $operation) { 76 | var_dump($operation->getStatus(), func_get_args()); 77 | });*/ 78 | 79 | //var_dump($disk->toArray()); 80 | 81 | //$resources = $disk->getResources(); 82 | //$resources->setMediaType('video'); 83 | 84 | //$resources = $disk->getTrashResources(); 85 | //$resources = $disk->cleanTrash(); 86 | //$resources = $disk->uploaded(); 87 | 88 | //$resources = $disk->getResource('disk:/applu.mp3'); 89 | //$resources = $disk->getResource('rich.txt'); 90 | 91 | //$resources = $disk->getTrashResources(1); 92 | //$resources->setSort('deleted', true); 93 | //$resources->setLimit(1, 1); 94 | 95 | //$resources = $disk->getTrashResource('/', 10); 96 | //$resources->setSort('deleted', false); 97 | 98 | /*$resources->addListener('operation', function () { 99 | var_dump('$resources', func_get_args()); 100 | });*/ 101 | 102 | //$result = $resources->create(); 103 | //$result = $resources->delete(false); 104 | //$resources->set('nama', rand()); 105 | //$resources->rollin = 'bakc'; 106 | //unset($resources['rollin']); 107 | //echo $resources->docviewer; 108 | //var_dump($disk->getOperation('0ec0c6a835b72b8860261cc2d5aaf26968b83b7f8eac3118b240eedd')->getStatus()); 109 | //$result = $resources->move('rich'); 110 | //$result = $resources->setPublish(true); 111 | 112 | //$result = $resources->download(__DIR__.'/data_big'); 113 | //$result = $resources->upload(__DIR__.'/data_big.data', true); 114 | //var_dump($result, $resources->toArray()); 115 | //var_dump($resources->toArray()); 116 | //var_dump($disk); -------------------------------------------------------------------------------- /src/Client/Container/ContainerTrait.php: -------------------------------------------------------------------------------- 1 | toArray()); 37 | } 38 | 39 | /** 40 | * Получить данные контейнера в виде массива 41 | * 42 | * @param array $allowed получить только эти ключи 43 | * 44 | * @return array контейнер 45 | */ 46 | public function toArray(array $allowed = null) 47 | { 48 | $contents = $this->store; 49 | 50 | if ($allowed !== null) 51 | { 52 | $contents = array_intersect_key($this->store, array_flip($allowed)); 53 | } 54 | 55 | /*foreach ($contents as $index => $value) 56 | { 57 | if ($value instanceof Container || $value instanceof AbstractResource) 58 | { 59 | $contents[$index] = $value->toArray(); 60 | } 61 | }*/ 62 | 63 | return $contents; 64 | } 65 | 66 | /** 67 | * Получить данные контейнера в виде объекта 68 | * 69 | * @param array $allowed получить только эти ключи 70 | * 71 | * @return \stdClass контейнер 72 | */ 73 | public function toObject(array $allowed = null) 74 | { 75 | return (object) $this->toArray($allowed); 76 | } 77 | 78 | /** 79 | * IteratorAggregate 80 | * 81 | * @return \IteratorAggregate итератор 82 | */ 83 | #[\ReturnTypeWillChange] 84 | public function getIterator() 85 | { 86 | return new \ArrayIterator($this->toArray()); 87 | } 88 | 89 | /** 90 | * Магический метод isset 91 | * 92 | * @return boolean 93 | */ 94 | public function __isset($key) 95 | { 96 | return $this->has($key); 97 | } 98 | 99 | /** 100 | * Проверить есть такой ключ в контейнере 101 | * 102 | * @param string $key 103 | * 104 | * @return bool 105 | */ 106 | public function has($key) 107 | { 108 | return $this->hasByIndex($key); 109 | } 110 | 111 | /** 112 | * Поиск по индексу 113 | * 114 | * @param $index 115 | * 116 | * @return bool 117 | */ 118 | protected function hasByIndex($index) 119 | { 120 | return array_key_exists($index, $this->toArray()); 121 | } 122 | 123 | /** 124 | * Магический метод get 125 | * 126 | * @return mixed 127 | */ 128 | public function __get($key) 129 | { 130 | return $this->get($key); 131 | } 132 | 133 | /** 134 | * Получить значение из контейнера по ключу 135 | * 136 | * @param string $index 137 | * @param mixed $default Может быть функцией. 138 | * 139 | * @return mixed 140 | */ 141 | public function get($index, $default = null) 142 | { 143 | if ($this->hasByIndex($index)) 144 | { 145 | return $this->store[$index]; 146 | } 147 | 148 | if ($default instanceof \Closure) 149 | { 150 | return $default($this); 151 | } 152 | 153 | return $default; 154 | } 155 | 156 | /** 157 | * Разрешает использование isset() 158 | * 159 | * @param string $key 160 | * 161 | * @return bool 162 | */ 163 | #[\ReturnTypeWillChange] 164 | public function offsetExists($key) 165 | { 166 | return $this->has($key); 167 | } 168 | 169 | /** 170 | * Разрешает доступ к ключам как к массиву 171 | * 172 | * @param string $key 173 | * 174 | * @return mixed 175 | * @throws \OutOfBoundsException 176 | */ 177 | #[\ReturnTypeWillChange] 178 | public function offsetGet($key) 179 | { 180 | return $this->get($key, function() use ($key) { 181 | throw new \OutOfBoundsException('Индекс не существует '.$key); 182 | }); 183 | } 184 | 185 | /** 186 | * Разрешает использование unset() 187 | * 188 | * @param string $key 189 | * 190 | * @return null 191 | * @throws \RuntimeException 192 | */ 193 | #[\ReturnTypeWillChange] 194 | public function offsetUnset($key) 195 | { 196 | return null; 197 | } 198 | 199 | /** 200 | * Разрешает обновление свойств объекта как массива 201 | * 202 | * @param string $key 203 | * @param mixed $value 204 | * 205 | * @return null 206 | */ 207 | #[\ReturnTypeWillChange] 208 | public function offsetSet($key, $value) 209 | { 210 | return null; 211 | } 212 | 213 | /** 214 | * Заменить все данные контейнера другими. 215 | * 216 | * @param array $content Новые данные. 217 | * 218 | * @return $this 219 | */ 220 | protected function setContents(array $content) 221 | { 222 | $this->store = $content; 223 | 224 | return $this; 225 | } 226 | 227 | } -------------------------------------------------------------------------------- /src/AbstractClient.php: -------------------------------------------------------------------------------- 1 | 'Arhitector\Yandex\Client\Exception\UnauthorizedException', 62 | 63 | /** 64 | * Доступ запрещён. Возможно, у приложения недостаточно прав для данного действия. 65 | */ 66 | 403 => 'Arhitector\Yandex\Client\Exception\ForbiddenException', 67 | 68 | /** 69 | * Не удалось найти запрошенный ресурс. 70 | */ 71 | 404 => 'Arhitector\Yandex\Client\Exception\NotFoundException' 72 | ]; 73 | 74 | /** 75 | * @var string для обращения к API требуется маркер доступа 76 | */ 77 | protected $tokenRequired = true; 78 | 79 | 80 | /** 81 | * Конструктор 82 | */ 83 | public function __construct() 84 | { 85 | $this->uri = new Uri(static::API_BASEPATH); 86 | $this->client = new PluginClient(new HttpClient(new DiactorosMessageFactory, new Factory, [ 87 | CURLOPT_SSL_VERIFYPEER => false 88 | ]), [ 89 | new RedirectPlugin 90 | ]); 91 | } 92 | 93 | /** 94 | * Текущий Uri 95 | * 96 | * @return \Psr\Http\Message\UriInterface|Uri 97 | */ 98 | public function getUri() 99 | { 100 | return $this->uri; 101 | } 102 | 103 | /** 104 | * Провести аунтификацию в соостветствии с типом сервиса 105 | * 106 | * @param \Psr\Http\Message\RequestInterface $request 107 | * 108 | * @return \Psr\Http\Message\RequestInterface 109 | */ 110 | abstract protected function authentication(RequestInterface $request); 111 | 112 | /** 113 | * Формат обмена данными 114 | * 115 | * @return string 116 | */ 117 | public function getContentType() 118 | { 119 | return $this->contentType; 120 | } 121 | 122 | /** 123 | * Модифицирует и отправляет запрос. 124 | * 125 | * @param \Psr\Http\Message\RequestInterface $request 126 | * 127 | * @return \Psr\Http\Message\ResponseInterface 128 | */ 129 | public function send(RequestInterface $request) 130 | { 131 | $request = $this->authentication($request); 132 | $defaultHeaders = [ 133 | 'Accept' => $this->getContentType(), 134 | 'Content-Type' => $this->getContentType() 135 | ]; 136 | 137 | foreach ($defaultHeaders as $defaultHeader => $value) { 138 | if (!$request->hasHeader($defaultHeader)) { 139 | $request = $request->withHeader($defaultHeader, $value); 140 | } 141 | } 142 | 143 | $response = $this->client->sendRequest($request); 144 | $response = $this->transformResponseToException($request, $response); 145 | 146 | return $response; 147 | } 148 | 149 | /** 150 | * Устаналивает необходимость токена при запросе. 151 | * 152 | * @param $tokenRequired 153 | * 154 | * @return boolean возвращает предыдущее состояние 155 | */ 156 | protected function setAccessTokenRequired($tokenRequired) 157 | { 158 | $previous = $this->tokenRequired; 159 | $this->tokenRequired = (bool) $tokenRequired; 160 | 161 | return $previous; 162 | } 163 | 164 | /** 165 | * Трансформирует ответ в исключения 166 | * 167 | * @param \Psr\Http\Message\RequestInterface $request 168 | * @param \Psr\Http\Message\ResponseInterface $response 169 | * 170 | * @return \Psr\Http\Message\ResponseInterface если статус код не является ошибочным, то вернуть объект ответа 171 | * @throws \Arhitector\Yandex\Client\Exception\ServiceException 172 | */ 173 | protected function transformResponseToException(RequestInterface $request, ResponseInterface $response) 174 | { 175 | if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 500) { 176 | throw new \RuntimeException($response->getReasonPhrase(), $response->getStatusCode()); 177 | } 178 | 179 | if ($response->getStatusCode() >= 500 && $response->getStatusCode() < 600) { 180 | throw new ServiceException($response->getReasonPhrase(), $response->getStatusCode()); 181 | } 182 | 183 | return $response; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/Disk/Resource/Removed.php: -------------------------------------------------------------------------------- 1 | setContents($path); 57 | $path = $path['path']; 58 | } 59 | 60 | if (!is_scalar($path)) { 61 | throw new \InvalidArgumentException('Параметр "path" должен быть строкового типа.'); 62 | } 63 | 64 | $this->path = (string) $path; 65 | $this->client = $parent; 66 | $this->uri = $uri; 67 | $this->setSort('created'); 68 | } 69 | 70 | /** 71 | * Получает информацию о ресурсе 72 | * 73 | * @return mixed 74 | */ 75 | public function toArray(array $allowed = null) 76 | { 77 | if (!$this->_toArray() || $this->isModified()) { 78 | $response = $this->client->send((new Request($this->uri->withPath($this->uri->getPath() . 'trash/resources') 79 | ->withQuery(http_build_query(array_merge($this->getParameters($this->parametersAllowed), [ 80 | 'path' => $this->getPath() 81 | ]), '', '&')), 'GET'))); 82 | 83 | if ($response->getStatusCode() == 200) { 84 | $response = json_decode($response->getBody(), true); 85 | 86 | if (!empty($response)) { 87 | $this->isModified = false; 88 | 89 | if (isset($response['_embedded'])) { 90 | $response = array_merge($response, $response['_embedded']); 91 | } 92 | 93 | unset($response['_links'], $response['_embedded']); 94 | 95 | if (isset($response['items'])) { 96 | $response['items'] = new Container\Collection(array_map(function ($item) { 97 | return new self($item, $this->client, $this->uri); 98 | }, $response['items'])); 99 | } 100 | 101 | $this->setContents($response); 102 | } 103 | } 104 | } 105 | 106 | return $this->_toArray($allowed); 107 | } 108 | 109 | /** 110 | * Восстановление файла или папки из Корзины 111 | * В корзине файлы с одинаковыми именами в действительности именют постфикс к имени в виде unixtime 112 | * 113 | * @param mixed $name оставляет имя как есть и если boolean это заменяет overwrite 114 | * @param boolean $overwrite 115 | * @return mixed 116 | */ 117 | public function restore($name = null, $overwrite = false) 118 | { 119 | if (is_bool($name)) { 120 | $overwrite = $name; 121 | $name = null; 122 | } 123 | 124 | if ($name instanceof Closed) { 125 | $name = $name->getPath(); 126 | } 127 | 128 | if (!empty($name) && !is_string($name)) { 129 | throw new \InvalidArgumentException('Новое имя для восстанавливаемого ресурса должо быть строкой'); 130 | } 131 | 132 | $request = new Request($this->uri->withPath($this->uri->getPath() . 'trash/resources/restore') 133 | ->withQuery(http_build_query([ 134 | 'path' => $this->getPath(), 135 | 'name' => (string) $name, 136 | 'overwrite' => (bool) $overwrite 137 | ], '', '&')), 'PUT'); 138 | 139 | $response = $this->client->send($request); 140 | 141 | if ($response->getStatusCode() == 201 || $response->getStatusCode() == 202) { 142 | $this->setContents([]); 143 | 144 | if ($response->getStatusCode() == 202) { 145 | $response = json_decode($response->getBody(), true); 146 | 147 | if (isset($response['operation'])) { 148 | $response['operation'] = $this->client->getOperation($response['operation']); 149 | $this->emit('operation', $response['operation'], $this, $this->client); 150 | $this->client->emit('operation', $response['operation'], $this, $this->client); 151 | 152 | return $response['operation']; 153 | } 154 | } 155 | 156 | return $this->client->getResource($name); 157 | } 158 | 159 | return false; 160 | } 161 | 162 | /** 163 | * Удаление файла или папки 164 | * 165 | * @return mixed 166 | */ 167 | public function delete() 168 | { 169 | try { 170 | $response = $this->client->send(new Request($this->uri->withPath($this->uri->getPath() . 'trash/resources') 171 | ->withQuery(http_build_query([ 172 | 'path' => $this->getPath() 173 | ], '', '&')), 'DELETE')); 174 | 175 | if ($response->getStatusCode() == 202) { 176 | $this->setContents([]); 177 | $response = json_decode($response->getBody(), true); 178 | 179 | if (!empty($response['operation'])) { 180 | $response['operation'] = $this->client->getOperation($response['operation']); 181 | $this->emit('operation', $response['operation'], $this, $this->client); 182 | $this->client->emit('operation', $response['operation'], $this, $this->client); 183 | 184 | return $response['operation']; 185 | } 186 | 187 | return true; 188 | } 189 | 190 | return $response->getStatusCode() == 204; 191 | } catch (\Exception $exc) { 192 | return false; 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /example/backup_dir_to_ydisk.php: -------------------------------------------------------------------------------- 1 | auth_token = $auth_token; 38 | $this->backup_src_dir = $backup_src_dir; 39 | $this->disk_dest_resrouce = $disk_dest_resource; 40 | 41 | $this->disk = new Arhitector\Yandex\Disk(); 42 | $this->disk->setAccessToken($auth_token); 43 | 44 | $this->is_md5_check_enabled = $verify_md5; 45 | $this->is_overwrite_enabled = $force_overwrite; 46 | $this->is_quiet_mod_enanled = $be_quiet; 47 | 48 | return $this; 49 | } 50 | 51 | public function run() 52 | { 53 | $this->iterateDirectory(''); 54 | } 55 | 56 | protected function backupFile($src_path, $dest_resource) 57 | { 58 | $this->info("Backing $src_path up to $dest_resource..."); 59 | $res = $this->disk->getResource($dest_resource); 60 | 61 | if ( $res->has() === true 62 | and $this->is_overwrite_enabled === false 63 | and $this->is_md5_check_enabled === false ) 64 | { 65 | $this->info("The resource $dest_resource exists. Please use overwrite flag to force upload"); 66 | return; 67 | } 68 | 69 | if ( $res->has() === false ) 70 | { 71 | $result = $res->upload($src_path); 72 | $this->info("Upload request status: $result"); 73 | } 74 | elseif ( $this->is_md5_check_enabled ) 75 | { 76 | $src_file_md5 = md5_file($src_path); 77 | $dest_file_md5 = $res->get('md5'); 78 | if ( strcmp($src_file_md5, $dest_file_md5) !== 0 ) 79 | { 80 | $this->warn("Failed md5 check: src [$src_file_md5] dest [$dest_file_md5]"); 81 | if ( $this->is_overwrite_enabled ) 82 | { 83 | $result = $res->upload($src_path, true); 84 | $this->info("Overwrite upload request status: $result"); 85 | } 86 | else 87 | { 88 | $this->warn("Please consider using overwrite flag to force upload."); 89 | } 90 | } 91 | else 92 | { 93 | $this->info("File is alreay on Disk and md5 is equal, skipping."); 94 | } 95 | } 96 | else 97 | { 98 | $this->info("File exists on Disk, md5 check is disabled, skipping."); 99 | } 100 | } 101 | 102 | protected function iterateDirectory($cur_rel_path) 103 | { 104 | $cur_dir = $this->backup_src_dir.$cur_rel_path; 105 | $dir_handle = opendir($cur_dir); 106 | $this->info('Iterating directory: '.$cur_dir); 107 | 108 | while ( $file = readdir($dir_handle) ) 109 | { 110 | if ($file[0] == '.') continue; // skip '.' '..' and hidden files 111 | 112 | $file_next = $cur_dir.DIRECTORY_SEPARATOR.$file; 113 | $target_resource_path = $this->disk_dest_resrouce.$cur_rel_path.'/'.$file; 114 | 115 | if ( is_dir($file_next) ) 116 | { 117 | 118 | $res = $this->disk->getResource($target_resource_path); 119 | 120 | if ( $res->has() === false ) 121 | { 122 | $this->info("The Dir resource '$target_resource_path' doesn't exist. Going to create one"); 123 | $res->create(); 124 | } 125 | 126 | $this->iterateDirectory($cur_rel_path.DIRECTORY_SEPARATOR.$file); 127 | } 128 | else 129 | { 130 | $this->backupFile($file_next, $target_resource_path); 131 | } 132 | } 133 | } 134 | 135 | public function log($level, $message, array $context = array()) 136 | { 137 | if ($this->is_quiet_mod_enanled) return; 138 | 139 | $pid = 'N/A'; 140 | if (function_exists('getmypid')) 141 | { 142 | $pid = getmypid(); 143 | } 144 | 145 | echo date('Y-m-d H:i:s').' ('.$pid.')'.' ['.$level.'] '.$message.(count($context) ? '. Context: '.var_export($context, true) : '')."\n"; 146 | } 147 | 148 | public function info($message, array $context = array()) 149 | { 150 | return $this->log('INFO', $message, $context); 151 | } 152 | 153 | public function warn($message, array $context = array()) 154 | { 155 | return $this->log('WARN', $message, $context); 156 | } 157 | } 158 | 159 | /** 160 | * Fetch options from the command line arguments 161 | */ 162 | 163 | $usage = "Usage: php ".basename(__FILE__)." --token=TOKEN src_backup_dir dest_ydisk_resource"; 164 | 165 | $longopts = array( 166 | "--token: YD authorisaztion token, required" => "token:", 167 | "--force: force overwrite, default 'no'" => "force::", 168 | "--verify: verify MD5 checksums, default 'no'" => "verify::", 169 | "--quiet: disable verbose output, default 'no'" => "quiet::", 170 | ); 171 | 172 | $options = getopt('', $longopts); 173 | 174 | if ($argc < 4 175 | or !array_key_exists('token', $options) or strlen($options['token']) < 32 176 | or $argv[$argc-2][0] == '-' or $argv[$argc-1][0] == '-') 177 | { 178 | echo "$usage\nAvailable options: \n"; 179 | foreach ($longopts as $opt_desc => $opt) 180 | { 181 | echo " $opt_desc\n"; 182 | } 183 | echo "\nExample:\n".basename(__FILE__)." --token=270d7f9b7e23a15a7 /var/local/database_dump/ disk:/Backups/DataBase\n\n"; 184 | exit(1); 185 | } 186 | 187 | $backup_src_dir = $argv[$argc-2]; 188 | $disk_dest_resource = $argv[$argc-1]; 189 | 190 | foreach (array('force', 'verify', 'quiet') as $option) 191 | { 192 | if (array_key_exists($option, $options)) 193 | { 194 | if ($options[$option] == 'yes') 195 | { 196 | $options[$option] = true; 197 | } 198 | } 199 | else 200 | { 201 | $options[$option] = false; 202 | } 203 | } 204 | 205 | /** 206 | * Do the backup with these options 207 | */ 208 | 209 | $ydBackup = new YDiskBackup($options['token'], realpath($backup_src_dir), $disk_dest_resource, $options['force'], $options['verify'], $options['quiet']); 210 | $ydBackup->run(); -------------------------------------------------------------------------------- /src/Client/OAuth.php: -------------------------------------------------------------------------------- 1 | setAccessToken($token); 57 | } 58 | } 59 | 60 | /** 61 | * Устанавливает ID приложения 62 | * 63 | * @param string $client_id 64 | * 65 | * @return $this 66 | * @throws \InvalidArgumentException 67 | */ 68 | public function setClientOauth($client_id) 69 | { 70 | if (!is_string($client_id)) { 71 | throw new \InvalidArgumentException('ID приложения https://oauth.yandex.ru должен быть строкового типа.'); 72 | } 73 | 74 | $this->clientOauth = $client_id; 75 | 76 | return $this; 77 | } 78 | 79 | /** 80 | * Возвращает ID приложения 81 | * 82 | * @return string 83 | */ 84 | public function getClientOauth() 85 | { 86 | return $this->clientOauth; 87 | } 88 | 89 | /** 90 | * Устанавливает пароль приложения 91 | * 92 | * @param string $client_secret 93 | * 94 | * @return $this 95 | * @throws \InvalidArgumentException 96 | */ 97 | public function setClientOauthSecret($client_secret) 98 | { 99 | if (!is_string($client_secret)) { 100 | throw new \InvalidArgumentException('Пароль приложения https://oauth.yandex.ru должен быть строкового типа.'); 101 | } 102 | 103 | $this->clientOauthSecret = $client_secret; 104 | 105 | return $this; 106 | } 107 | 108 | /** 109 | * Возвращает пароль приложения 110 | * 111 | * @return string 112 | */ 113 | public function getClientOauthSecret() 114 | { 115 | return $this->clientOauthSecret; 116 | } 117 | 118 | /** 119 | * Устанавливает OAuth-токен. 120 | * 121 | * @param string $token 122 | * 123 | * @return $this 124 | * @throws \InvalidArgumentException 125 | */ 126 | public function setAccessToken($token) 127 | { 128 | if (!is_string($token)) { 129 | throw new \InvalidArgumentException('OAuth-токен должен быть строкового типа.'); 130 | } 131 | 132 | $this->token = $token; 133 | 134 | return $this; 135 | } 136 | 137 | /** 138 | * Получает установленный токен 139 | * 140 | * @return string 141 | */ 142 | public function getAccessToken() 143 | { 144 | return (string) $this->token; 145 | } 146 | 147 | /** 148 | * Запрашивает или обновляет токен 149 | * 150 | * @param string $username имя пользователя yandex 151 | * @param string $password пароль от аккаунта 152 | * @param bool $onlyToken вернуть только строковый токен 153 | * 154 | * @return object|string 155 | * @throws UnauthorizedException 156 | * @throws \Exception 157 | * @example 158 | * 159 | * $client->refreshAccessToken('username', 'password'); 160 | * 161 | * object(stdClass)[28] 162 | * public 'token_type' => string 'bearer' (length=6) 163 | * public 'access_token' => string 'c7621`6b09032dwf9a6a7ca765eb39b8' (length=32) 164 | * public 'expires_in' => int 31536000 165 | * public 'uid' => int 241`68329 166 | * public 'created_at' => int 1456882032 167 | * 168 | * @example 169 | * 170 | * $client->refreshAccessToken('username', 'password', true); 171 | * 172 | * string 'c7621`6b09032dwf9a6a7ca765eb39b8' (length=32) 173 | */ 174 | public function refreshAccessToken($username, $password, $onlyToken = false) 175 | { 176 | if (!is_scalar($username) || !is_scalar($password)) { 177 | throw new \InvalidArgumentException('Параметры "имя пользователя" и "пароль" должны быть простого типа.'); 178 | } 179 | 180 | $previous = $this->setAccessTokenRequired(false); 181 | $request = new Request(rtrim(self::API_BASEPATH, ' /') . '/token', 'POST'); 182 | $request->getBody() 183 | ->write(http_build_query([ 184 | 'grant_type' => 'password', 185 | 'client_id' => $this->getClientOauth(), 186 | 'client_secret' => $this->getClientOauthSecret(), 187 | 'username' => (string) $username, 188 | 'password' => (string) $password 189 | ])); 190 | 191 | try { 192 | $response = json_decode($this->send($request)->wait()->getBody()); 193 | 194 | if ($onlyToken) { 195 | return (string) $response->access_token; 196 | } 197 | 198 | $response->created_at = time(); 199 | 200 | return $response; 201 | } catch (\Exception $exc) { 202 | $response = json_decode($exc->getMessage()); 203 | 204 | if (isset($response->error_description)) { 205 | throw new UnauthorizedException($response->error_description); 206 | } 207 | 208 | throw $exc; 209 | } finally { 210 | $this->setAccessTokenRequired($previous); 211 | } 212 | } 213 | 214 | /** 215 | * Провести аунтификацию в соостветствии с типом сервиса 216 | * 217 | * @param \Psr\Http\Message\RequestInterface $request 218 | * 219 | * @return \Psr\Http\Message\RequestInterface 220 | */ 221 | protected function authentication(RequestInterface $request) 222 | { 223 | if ($this->tokenRequired) { 224 | return $request->withHeader('Authorization', sprintf('OAuth %s', $this->getAccessToken())); 225 | } 226 | 227 | return $request; 228 | } 229 | 230 | /** 231 | * Трансформирует ответ в исключения. 232 | * Ответ API, где использует OAuth, отличается от других сервисов. 233 | * 234 | * @param \Psr\Http\Message\RequestInterface $request 235 | * @param \Psr\Http\Message\ResponseInterface $response 236 | * 237 | * @return \Psr\Http\Message\ResponseInterface если статус код не является ошибочным, то вернуть объект ответа 238 | */ 239 | protected function transformResponseToException(RequestInterface $request, ResponseInterface $response) 240 | { 241 | if (isset($this->exceptions[$response->getStatusCode()])) { 242 | $exception = $this->exceptions[$response->getStatusCode()]; 243 | 244 | if ( 245 | $response->hasHeader('Content-Type') 246 | && stripos($response->getHeaderLine('Content-Type'), 'json') !== false 247 | ) { 248 | $responseBody = json_decode($response->getBody(), true); 249 | 250 | if (!isset($responseBody['message'])) { 251 | $responseBody['message'] = (string) $response->getBody(); 252 | } 253 | 254 | if (is_array($exception)) { 255 | if (!isset($responseBody['error'], $exception[$responseBody['error']])) { 256 | return parent::transformResponseToException($request, $response); 257 | } 258 | 259 | $exception = $exception[$responseBody['error']]; 260 | } 261 | 262 | throw new $exception($responseBody['message'], $response->getStatusCode()); 263 | } 264 | } 265 | 266 | return parent::transformResponseToException($request, $response); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/Client/HttpClient.php: -------------------------------------------------------------------------------- 1 | handle = curl_init(); 87 | $this->messageFactory = $messageFactory; 88 | $this->streamFactory = $streamFactory; 89 | $this->options = $options; 90 | } 91 | 92 | /** 93 | * Release resources if still active 94 | */ 95 | public function __destruct() 96 | { 97 | if (is_resource($this->handle)) 98 | { 99 | curl_close($this->handle); 100 | } 101 | } 102 | 103 | /** 104 | * Sends a PSR-7 request and returns a PSR-7 response. 105 | * 106 | * @param RequestInterface $request 107 | * 108 | * @return ResponseInterface 109 | * 110 | * @throws \RuntimeException If creating the body stream fails. 111 | * @throws \UnexpectedValueException if unsupported HTTP version requested 112 | * @throws RequestException 113 | */ 114 | public function sendRequest(RequestInterface $request): ResponseInterface 115 | { 116 | $responseBuilder = $this->createResponseBuilder(); 117 | $options = $this->createCurlOptions($request, $responseBuilder); 118 | 119 | curl_reset($this->handle); 120 | curl_setopt_array($this->handle, $options); 121 | curl_exec($this->handle); 122 | 123 | if (curl_errno($this->handle) > 0) { 124 | throw new RequestException(curl_error($this->handle), $request); 125 | } 126 | 127 | $response = $responseBuilder->getResponse(); 128 | $response->getBody()->seek(0); 129 | 130 | return $response; 131 | } 132 | 133 | /** 134 | * Sends a PSR-7 request in an asynchronous way. 135 | * 136 | * @param RequestInterface $request 137 | * 138 | * @return Promise 139 | * 140 | * @throws \RuntimeException If creating the body stream fails. 141 | * @throws \UnexpectedValueException If unsupported HTTP version requested 142 | * @throws Exception 143 | * 144 | * @since 1.0 145 | */ 146 | public function sendAsyncRequest(RequestInterface $request) 147 | { 148 | if ( ! $this->multiRunner instanceof MultiRunner) 149 | { 150 | $this->multiRunner = new MultiRunner(); 151 | } 152 | 153 | $handle = curl_init(); 154 | $responseBuilder = $this->createResponseBuilder(); 155 | $options = $this->createCurlOptions($request, $responseBuilder); 156 | curl_setopt_array($handle, $options); 157 | 158 | $core = new PromiseCore($request, $handle, $responseBuilder); 159 | $promise = new CurlPromise($core, $this->multiRunner); 160 | $this->multiRunner->add($core); 161 | 162 | return $promise; 163 | } 164 | 165 | /** 166 | * Generates cURL options 167 | * 168 | * @param RequestInterface $request 169 | * @param ResponseBuilder $responseBuilder 170 | * 171 | * @throws \UnexpectedValueException if unsupported HTTP version requested 172 | * @throws \RuntimeException if can not read body 173 | * 174 | * @return array 175 | */ 176 | protected function createCurlOptions(RequestInterface $request, ResponseBuilder $responseBuilder) 177 | { 178 | $options = array_diff_key($this->options, array_flip([CURLOPT_INFILE, CURLOPT_INFILESIZE])); 179 | $options[CURLOPT_HTTP_VERSION] = $this->getCurlHttpVersion($request->getProtocolVersion()); 180 | $options[CURLOPT_HEADERFUNCTION] = function ($ch, $data) use ($responseBuilder) { 181 | $str = trim($data); 182 | 183 | if ('' !== $str) 184 | { 185 | if (strpos(strtolower($str), 'http/') === 0) 186 | { 187 | $responseBuilder->setStatus($str)->getResponse(); 188 | } 189 | else 190 | { 191 | $responseBuilder->addHeader($str); 192 | } 193 | } 194 | 195 | return strlen($data); 196 | }; 197 | 198 | $options[CURLOPT_CUSTOMREQUEST] = $request->getMethod(); 199 | $options[CURLOPT_URL] = (string) $request->getUri(); 200 | $options[CURLOPT_HEADER] = false; 201 | 202 | if (in_array($request->getMethod(), ['GET', 'HEAD', 'TRACE', 'CONNECT'])) 203 | { 204 | if ($request->getMethod() == 'HEAD') 205 | { 206 | $options[CURLOPT_NOBODY] = true; 207 | 208 | unset($options[CURLOPT_READFUNCTION], $options[CURLOPT_WRITEFUNCTION]); 209 | } 210 | } 211 | else 212 | { 213 | $options = $this->createCurlBody($request, $options); 214 | } 215 | 216 | $options[CURLOPT_WRITEFUNCTION] = function ($ch, $data) use ($responseBuilder) { 217 | return $responseBuilder->getResponse()->getBody()->write($data); 218 | }; 219 | 220 | $options[CURLOPT_HTTPHEADER] = $this->createHeaders($request, $options); 221 | 222 | if ($request->getUri()->getUserInfo()) 223 | { 224 | $options[CURLOPT_USERPWD] = $request->getUri()->getUserInfo(); 225 | } 226 | 227 | $options[CURLOPT_FOLLOWLOCATION] = false; 228 | 229 | return $options; 230 | } 231 | 232 | /** 233 | * Create headers array for CURLOPT_HTTPHEADER 234 | * 235 | * @param RequestInterface $request 236 | * @param array $options cURL options 237 | * 238 | * @return string[] 239 | */ 240 | protected function createHeaders(RequestInterface $request, array $options) 241 | { 242 | $headers = []; 243 | $body = $request->getBody(); 244 | $size = $body->getSize(); 245 | 246 | foreach ($request->getHeaders() as $header => $values) 247 | { 248 | foreach ((array) $values as $value) 249 | { 250 | $headers[] = sprintf('%s: %s', $header, $value); 251 | } 252 | } 253 | 254 | if ( ! $request->hasHeader('Transfer-Encoding') && $size === null) 255 | { 256 | $headers[] = 'Transfer-Encoding: chunked'; 257 | } 258 | 259 | if ( ! $request->hasHeader('Expect') && in_array($request->getMethod(), ['POST', 'PUT'])) 260 | { 261 | if ($request->getProtocolVersion() < 2.0 && ! $body->isSeekable() || $size === null || $size > 1048576) 262 | { 263 | $headers[] = 'Expect: 100-Continue'; 264 | } 265 | else 266 | { 267 | $headers[] = 'Expect: '; 268 | } 269 | } 270 | 271 | return $headers; 272 | } 273 | 274 | /** 275 | * Create body 276 | * 277 | * @param RequestInterface $request 278 | * @param array $options 279 | * 280 | * @return array 281 | */ 282 | protected function createCurlBody(RequestInterface $request, array $options) 283 | { 284 | $body = clone $request->getBody(); 285 | $size = $body->getSize(); 286 | 287 | // Avoid full loading large or unknown size body into memory. It doesn't replace "CURLOPT_READFUNCTION". 288 | if ($size === null || $size > 1048576) 289 | { 290 | if ($body->isSeekable()) 291 | { 292 | $body->rewind(); 293 | } 294 | 295 | $options[CURLOPT_UPLOAD] = true; 296 | 297 | if (isset($options[CURLOPT_READFUNCTION]) && is_callable($options[CURLOPT_READFUNCTION])) 298 | { 299 | $body = $body->detach(); 300 | 301 | $options[CURLOPT_READFUNCTION] = function($curl_handler, $handler, $length) use ($body, $options) { 302 | return call_user_func($options[CURLOPT_READFUNCTION], $curl_handler, $body, $length); 303 | }; 304 | } 305 | else 306 | { 307 | $options[CURLOPT_READFUNCTION] = function($curl, $handler, $length) use ($body) { 308 | return $body->read($length); 309 | }; 310 | } 311 | } 312 | else 313 | { 314 | $options[CURLOPT_POSTFIELDS] = (string) $request->getBody(); 315 | } 316 | 317 | return $options; 318 | } 319 | 320 | /** 321 | * Return cURL constant for specified HTTP version 322 | * 323 | * @param string $version 324 | * 325 | * @throws \UnexpectedValueException if unsupported version requested 326 | * 327 | * @return int 328 | */ 329 | protected function getCurlHttpVersion($version) 330 | { 331 | if ($version == '1.1') 332 | { 333 | return CURL_HTTP_VERSION_1_1; 334 | } 335 | else 336 | { 337 | if ($version == '2.0') 338 | { 339 | if ( ! defined('CURL_HTTP_VERSION_2_0')) 340 | { 341 | throw new \UnexpectedValueException('libcurl 7.33 needed for HTTP 2.0 support'); 342 | } 343 | 344 | return CURL_HTTP_VERSION_2_0; 345 | } 346 | else 347 | { 348 | return CURL_HTTP_VERSION_1_0; 349 | } 350 | } 351 | } 352 | 353 | /** 354 | * Create new ResponseBuilder instance 355 | * 356 | * @return ResponseBuilder 357 | * 358 | * @throws \RuntimeException If creating the stream from $body fails. 359 | */ 360 | protected function createResponseBuilder() 361 | { 362 | try 363 | { 364 | $body = $this->streamFactory->createStream(fopen('php://temp', 'w+')); 365 | } 366 | catch (\InvalidArgumentException $e) 367 | { 368 | throw new \RuntimeException('Can not create "php://temp" stream.'); 369 | } 370 | 371 | $response = $this->messageFactory->createResponse(200, null, [], $body); 372 | 373 | return new ResponseBuilder($response); 374 | } 375 | 376 | } -------------------------------------------------------------------------------- /src/Disk/Resource/Opened.php: -------------------------------------------------------------------------------- 1 | setContents($public_key); 66 | $public_key = $public_key['public_key']; 67 | $this->store['docviewer'] = $this->createDocViewerUrl(); 68 | } 69 | 70 | if (!is_scalar($public_key)) { 71 | throw new \InvalidArgumentException('Параметр "public_key" должен быть строкового типа.'); 72 | } 73 | 74 | $this->publicKey = (string) $public_key; 75 | $this->client = $parent; 76 | $this->uri = $uri; 77 | } 78 | 79 | /** 80 | * Получить публичный ключ 81 | * 82 | * @return string|null 83 | */ 84 | public function getPublicKey() 85 | { 86 | return $this->publicKey; 87 | } 88 | 89 | /** 90 | * Получает информацию о ресурсе 91 | * 92 | * @return mixed 93 | */ 94 | public function toArray(array $allowed = null) 95 | { 96 | if (!$this->_toArray() || $this->isModified()) { 97 | $response = $this->client->send((new Request($this->uri->withPath($this->uri->getPath() . 'public/resources') 98 | ->withQuery(http_build_query(array_merge($this->getParameters($this->parametersAllowed), [ 99 | 'public_key' => $this->getPublicKey() 100 | ]), '', '&')), 'GET'))); 101 | 102 | if ($response->getStatusCode() == 200) { 103 | $response = json_decode($response->getBody(), true); 104 | 105 | if (!empty($response)) { 106 | $this->isModified = false; 107 | 108 | if (isset($response['_embedded'])) { 109 | $response = array_merge($response, $response['_embedded']); 110 | } 111 | 112 | unset($response['_links'], $response['_embedded']); 113 | 114 | if (isset($response['items'])) { 115 | $response['items'] = new Container\Collection(array_map(function ($item) { 116 | return new self($item, $this->client, $this->uri); 117 | }, $response['items'])); 118 | } 119 | 120 | $this->setContents($response); 121 | $this->store['docviewer'] = $this->createDocViewerUrl(); 122 | } 123 | } 124 | } 125 | 126 | return $this->_toArray($allowed); 127 | } 128 | 129 | /** 130 | * Получает прямую ссылку 131 | * 132 | * @return string 133 | * @throws mixed 134 | * 135 | * @TODO Не работает для файлов вложенных в публичную папку. 136 | */ 137 | public function getLink() 138 | { 139 | if (!$this->has()) { 140 | throw new NotFoundException('Не удалось найти запрошенный ресурс.'); 141 | } 142 | 143 | $response = $this->client->send(new Request($this->uri->withPath($this->uri->getPath() . 'public/resources/download') 144 | ->withQuery(http_build_query([ 145 | 'public_key' => $this->getPublicKey(), 146 | 'path' => (string) $this->getPath() 147 | ], '', '&')), 'GET')); 148 | 149 | if ($response->getStatusCode() == 200) { 150 | $response = json_decode($response->getBody(), true); 151 | 152 | if (isset($response['href'])) { 153 | return $response['href']; 154 | } 155 | } 156 | 157 | throw new \UnexpectedValueException('Не удалось запросить разрешение на скачивание, повторите заново'); 158 | } 159 | 160 | /** 161 | * Скачивание публичного файла или папки 162 | * 163 | * @param resource|StreamInterface|string $destination Путь, по которому будет сохранён файл 164 | * StreamInterface будет записан в поток 165 | * resource открытый на запись 166 | * @param boolean $overwrite флаг перезаписи 167 | * @param boolean $check_hash провести проверку целостности скачанного файла 168 | * на основе хэша MD5 169 | * 170 | * @return bool 171 | */ 172 | public function download($destination, $overwrite = false, $check_hash = false) 173 | { 174 | $destination_type = gettype($destination); 175 | 176 | if (is_resource($destination)) { 177 | $destination = new Stream($destination); 178 | } 179 | 180 | if ($destination instanceof StreamInterface) { 181 | if (!$destination->isWritable()) { 182 | throw new \OutOfBoundsException('Дескриптор файла должен быть открыт с правами на запись.'); 183 | } 184 | } else if ($destination_type == 'string') { 185 | if (is_file($destination) && !$overwrite) { 186 | throw new AlreadyExistsException('По указанному пути "' . $destination . '" уже существует ресурс.'); 187 | } 188 | 189 | if (!is_writable(dirname($destination))) { 190 | throw new \OutOfBoundsException('Запрещена запись в директорию, в которой должен быть расположен файл.'); 191 | } 192 | 193 | $destination = new Stream($destination, 'w+b'); 194 | } else { 195 | throw new \InvalidArgumentException('Такой тип параметра $destination не поддерживается.'); 196 | } 197 | 198 | $response = $this->client->send(new Request($this->getLink(), 'GET')); 199 | 200 | if ($response->getStatusCode() == 200) { 201 | $stream = $response->getBody(); 202 | 203 | if ($check_hash) { 204 | $ctx = hash_init('md5'); 205 | 206 | while (!$stream->eof()) { 207 | $read_data = $stream->read(1048576); 208 | $destination->write($read_data); 209 | 210 | hash_update($ctx, $read_data); 211 | } 212 | } else { 213 | while (!$stream->eof()) { 214 | $destination->write($stream->read(16384)); 215 | } 216 | } 217 | 218 | $stream->close(); 219 | $this->emit('downloaded', $this, $destination, $this->client); 220 | $this->client->emit('downloaded', $this, $destination, $this->client); 221 | 222 | if ($destination_type == 'object') { 223 | return $destination; 224 | } else if ($check_hash && $destination_type == 'string' && $this->isFile()) { 225 | if (hash_final($ctx, false) !== $this->get('md5', null)) { 226 | throw new \RangeException('Ресурс скачан, но контрольные суммы различаются.'); 227 | } 228 | } 229 | 230 | return $destination->getSize(); 231 | } 232 | 233 | return false; 234 | } 235 | 236 | /** 237 | * Этот файл или такой же находится на моём диске 238 | * Метод требует Access Token 239 | * 240 | * @return boolean 241 | */ 242 | public function hasEqual() 243 | { 244 | if ($this->has() && ($path = $this->get('name'))) { 245 | try { 246 | return $this->client->getResource(((string) $this->get('path')) . '/' . $path) 247 | ->get('md5', false) === $this->get('md5'); 248 | } catch (\Exception $exc) { 249 | } 250 | } 251 | 252 | return false; 253 | } 254 | 255 | /** 256 | * Сохранение публичного файла в «Загрузки» или отдельный файл из публичной папки 257 | * 258 | * @param string $name Имя, под которым файл следует сохранить в папку «Загрузки» 259 | * @param string $path Путь внутри публичной папки. 260 | * 261 | * @return mixed 262 | */ 263 | public function save($name = null, $path = null) 264 | { 265 | $parameters = []; 266 | 267 | /** 268 | * @var mixed $name Имя, под которым файл следует сохранить в папку «Загрузки» 269 | */ 270 | if (is_string($name)) { 271 | $parameters['name'] = $name; 272 | } else if ($name instanceof Closed) { 273 | $parameters['name'] = substr(strrchr($name->getPath(), '/'), 1); 274 | } 275 | 276 | /** 277 | * @var string $path (необязательный) 278 | * Путь внутри публичной папки. Следует указать, если в значении параметра public_key передан 279 | * ключ публичной папки, в которой находится нужный файл. 280 | * Путь в значении параметра следует кодировать в URL-формате. 281 | */ 282 | if (is_string($path)) { 283 | $parameters['path'] = $path; 284 | } else if ($this->getPath() !== null) { 285 | $parameters['path'] = $this->getPath(); 286 | } 287 | 288 | /** 289 | * Если к моменту ответа запрос удалось обработать без ошибок, API отвечает кодом 201 Created и возвращает 290 | * ссылку на сохраненный файл в теле ответа (в объекте Link). 291 | * Если операция сохранения была запущена, но еще не завершилась, Яндекс.Диск отвечает кодом 202 Accepted. 292 | */ 293 | $response = $this->client->send((new Request($this->uri->withPath($this->uri->getPath() 294 | . 'public/resources/save-to-disk') 295 | ->withQuery(http_build_query([ 296 | 'public_key' => $this->getPublicKey() 297 | ] + $parameters, '', '&')), 'POST'))); 298 | 299 | if ($response->getStatusCode() == 202 || $response->getStatusCode() == 201) { 300 | $response = json_decode($response->getBody(), true); 301 | 302 | if (isset($response['operation'])) { 303 | $response['operation'] = $this->client->getOperation($response['operation']); 304 | $this->emit('operation', $response['operation'], $this, $this->client); 305 | $this->client->emit('operation', $response['operation'], $this, $this->client); 306 | 307 | return $response['operation']; 308 | } 309 | 310 | if (isset($response['href'])) { 311 | parse_str((new Uri($response['href']))->getQuery(), $path); 312 | 313 | if (isset($path['path'])) { 314 | return $this->client->getResource($path['path']); 315 | } 316 | } 317 | } 318 | 319 | return false; 320 | } 321 | 322 | /** 323 | * Устанавливает путь внутри публичной папки 324 | * 325 | * @param string $path 326 | * 327 | * @return $this 328 | */ 329 | public function setPath($path) 330 | { 331 | if (!is_scalar($path)) { 332 | throw new \InvalidArgumentException('Параметр "path" должен быть строкового типа.'); 333 | } 334 | 335 | $this->path = (string) $path; 336 | 337 | return $this; 338 | } 339 | 340 | /** 341 | * Получает ссылку для просмотра документа. 342 | * 343 | * @return bool|string 344 | * @throws \InvalidArgumentException 345 | */ 346 | protected function createDocViewerUrl() 347 | { 348 | if ($this->isFile()) { 349 | $docviewer = [ 350 | 'name' => $this->get('name'), 351 | 'url' => sprintf('ya-disk-public://%s', $this->get('public_key')) 352 | ]; 353 | 354 | return (string) (new Uri('https://docviewer.yandex.ru/')) 355 | ->withQuery(http_build_query($docviewer, '', '&')); 356 | } 357 | 358 | return false; 359 | } 360 | } 361 | -------------------------------------------------------------------------------- /src/Disk.php: -------------------------------------------------------------------------------- 1 | 'Arhitector\Yandex\Client\Exception\UnsupportedException', 51 | 52 | /** 53 | * Не авторизован (Unauthorized). 54 | */ 55 | 401 => 'Arhitector\Yandex\Client\Exception\UnauthorizedException', 56 | 57 | /** 58 | * Доступ запрещён (Forbidden). 59 | * Возможно, у приложения недостаточно прав для данного действия. 60 | */ 61 | 403 => 'Arhitector\Yandex\Client\Exception\ForbiddenException', 62 | 63 | /** 64 | * Не удалось найти запрошенный ресурс (Not Found). 65 | */ 66 | 404 => 'Arhitector\Yandex\Client\Exception\NotFoundException', 67 | 68 | /** 69 | * Ресурс не может быть представлен в запрошенном формате (Not Acceptable). 70 | */ 71 | 406 => 'Arhitector\Yandex\Disk\Exception\UnsupportedException', 72 | 73 | /** 74 | * Конфликт путей/имён. 75 | */ 76 | 409 => [ 77 | 78 | /** 79 | * Указанного пути не существует. 80 | */ 81 | 'DiskPathDoesntExistsError' => 'Arhitector\Yandex\Client\Exception\NotFoundException', 82 | 83 | /** 84 | * Ресурс уже существует 85 | */ 86 | 'DiskResourceAlreadyExistsError' => 'Arhitector\Yandex\Disk\Exception\AlreadyExistsException', 87 | 88 | /** 89 | * Уже существует папка с таким именем. 90 | */ 91 | 'DiskPathPointsToExistentDirectoryError' => 'Arhitector\Yandex\Disk\Exception\AlreadyExistsException' 92 | ], 93 | 94 | /** 95 | * Ресурс не может быть представлен в запрошенном формате (Unsupported Media Type). 96 | */ 97 | 415 => 'Arhitector\Yandex\Client\Exception\UnsupportedException', 98 | 99 | /** 100 | * Ресурс заблокирован (Locked). 101 | * Возможно, над ним выполняется другая операция. 102 | */ 103 | 423 => 'Arhitector\Yandex\Client\Exception\ForbiddenException', 104 | 105 | /** 106 | * Слишком много запросов(Too Many Requests). 107 | */ 108 | 429 => 'Arhitector\Yandex\Client\Exception\ForbiddenException', 109 | 110 | /** 111 | * Сервис временно недоступен(Service Unavailable). 112 | */ 113 | 503 => 'Arhitector\Yandex\Client\Exception\ServiceException', 114 | 115 | /** 116 | * Недостаточно свободного места (Insufficient Storage). 117 | */ 118 | 507 => 'Arhitector\Yandex\Disk\Exception\OutOfSpaceException' 119 | ]; 120 | 121 | /** 122 | * @var array идентификаторы операций за сессию 123 | */ 124 | protected $operations = []; 125 | 126 | /** 127 | * @var string имя класса коллекции ресурсов 128 | */ 129 | protected $collectionClass = Disk\Resource\Collection::class; 130 | 131 | /** 132 | * Конструктор 133 | * 134 | * @param mixed $token маркер доступа 135 | * 136 | * @throws \InvalidArgumentException 137 | * 138 | * @example 139 | * 140 | * new Disk('token') 141 | * new Disk() -> setAccessToken('token') 142 | * new Disk( new Client('token') ) 143 | */ 144 | public function __construct($token = null) 145 | { 146 | $this->setEmitter(new Emitter); 147 | 148 | if ($token instanceof AbstractClient) { 149 | $token = $token->getAccessToken(); 150 | } 151 | 152 | parent::__construct($token); 153 | } 154 | 155 | /** 156 | * Получает информацию о диске 157 | * 158 | * @param array $allowed 159 | * 160 | * @return array 161 | * @example 162 | * 163 | * array (size=5) 164 | * 'trash_size' => int 9449304 165 | * 'total_space' => float 33822867456 166 | * 'used_space' => float 25863284099 167 | * 'free_space' => float 7959583357 168 | * 'system_folders' => array (size=2) 169 | * 'applications' => string 'disk:/Приложения' (length=26) 170 | * 'downloads' => string 'disk:/Загрузки/' (length=23) 171 | */ 172 | public function toArray(array $allowed = null) 173 | { 174 | if (!$this->_toArray()) { 175 | $response = $this->send(new Request($this->uri, 'GET')); 176 | 177 | if ($response->getStatusCode() == 200) { 178 | $response = json_decode($response->getBody(), true); 179 | 180 | if (!is_array($response)) { 181 | throw new UnsupportedException('Получен не поддерживаемый формат ответа от API Диска.'); 182 | } 183 | 184 | $this->setContents($response += [ 185 | 'free_space' => $response['total_space'] - $response['used_space'] 186 | ]); 187 | } 188 | } 189 | 190 | return $this->_toArray($allowed); 191 | } 192 | 193 | /** 194 | * Работа с ресурсами на диске 195 | * 196 | * @param string $path Путь к новому либо уже существующему ресурсу 197 | * @param integer $limit 198 | * @param integer $offset 199 | * 200 | * @return \Arhitector\Yandex\Disk\Resource\Closed 201 | * 202 | * @example 203 | * 204 | * $disk->getResource('any_file.ext') -> upload( __DIR__.'/file_to_upload'); 205 | * $disk->getResource('any_file.ext') // Mackey\Yandex\Disk\Resource\Closed 206 | * ->toArray(); // если ресурса еще нет, то исключение NotFoundException 207 | * 208 | * array (size=11) 209 | * 'public_key' => string 'wICbu9SPnY3uT4tFA6P99YXJwuAr2TU7oGYu1fTq68Y=' (length=44) 210 | * 'name' => string 'Gameface - Gangsigns_trapsound.ru.mp3' (length=37) 211 | * 'created' => string '2014-10-08T22:13:49+00:00' (length=25) 212 | * 'public_url' => string 'https://yadi.sk/d/g0N4hNtXcrq22' (length=31) 213 | * 'modified' => string '2014-10-08T22:13:49+00:00' (length=25) 214 | * 'media_type' => string 'audio' (length=5) 215 | * 'path' => string 'disk:/applications_swagga/1/Gameface - Gangsigns_trapsound.ru.mp3' (length=65) 216 | * 'md5' => string '8c2559f3ce1ece12e749f9e5dfbda59f' (length=32) 217 | * 'type' => string 'file' (length=4) 218 | * 'mime_type' => string 'audio/mpeg' (length=10) 219 | * 'size' => int 8099883 220 | */ 221 | public function getResource($path, $limit = 20, $offset = 0) 222 | { 223 | if (!is_string($path)) { 224 | throw new \InvalidArgumentException('Ресурс, должен быть строкового типа - путь к файлу/папке.'); 225 | } 226 | 227 | if (stripos($path, 'app:/') !== 0 && stripos($path, 'disk:/') !== 0) { 228 | $path = 'disk:/' . ltrim($path, ' /'); 229 | } 230 | 231 | return (new Disk\Resource\Closed($path, $this, $this->uri)) 232 | ->setLimit($limit, $offset); 233 | } 234 | 235 | /** 236 | * Список всех файлов. 237 | * 238 | * @param int $limit 239 | * @param int $offset 240 | * 241 | * @return \Arhitector\Yandex\Disk\Resource\Collection 242 | * 243 | * @example 244 | * 245 | * $disk->getResources(100, 0) // Arhitector\Yandex\Disk\Resource\Collection 246 | * ->toArray(); 247 | * 248 | * array (size=2) 249 | * 0 => object(Arhitector\Yandex\Disk\Resource\Closed)[30] 250 | * ..... 251 | */ 252 | public function getResources($limit = 20, $offset = 0) 253 | { 254 | $callback = function ($parameters) { 255 | $response = $this->send( 256 | new Request( 257 | $this->uri 258 | ->withPath($this->uri->getPath() . 'resources/files') 259 | ->withQuery(http_build_query($parameters, '', '&')), 260 | 'GET' 261 | ) 262 | ); 263 | 264 | if ($response->getStatusCode() == 200) { 265 | $response = json_decode($response->getBody(), true); 266 | 267 | if (isset($response['items'])) { 268 | return array_map(function ($item) { 269 | return new Disk\Resource\Closed($item, $this, $this->uri); 270 | }, $response['items']); 271 | } 272 | } 273 | 274 | return []; 275 | }; 276 | 277 | return (new $this->collectionClass($callback))->setLimit($limit, $offset); 278 | } 279 | 280 | /** 281 | * Работа с опубликованными ресурсами 282 | * 283 | * @param mixed $public_key Публичный ключ к опубликованному ресурсу. 284 | * 285 | * @return \Arhitector\Yandex\Disk\Resource\Opened 286 | * 287 | * @example 288 | * 289 | * $disk->getPublishResource('public_key') -> toArray() 290 | * 291 | * array (size=11) 292 | * 'public_key' => string 'wICbu9SPnY3uT4tFA6P99YXJwuAr2TU7oGYu1fTq68Y=' (length=44) 293 | * 'name' => string 'Gameface - Gangsigns_trapsound.ru.mp3' (length=37) 294 | * 'created' => string '2014-10-08T22:13:49+00:00' (length=25) 295 | * 'public_url' => string 'https://yadi.sk/d/g0N4hNtXcrq22' (length=31) 296 | * 'modified' => string '2014-10-08T22:13:49+00:00' (length=25) 297 | * 'media_type' => string 'audio' (length=5) 298 | * 'path' => string 'disk:/applications_swagga/1/Gameface - Gangsigns_trapsound.ru.mp3' (length=65) 299 | * 'md5' => string '8c2559f3ce1ece12e749f9e5dfbda59f' (length=32) 300 | * 'type' => string 'file' (length=4) 301 | * 'mime_type' => string 'audio/mpeg' (length=10) 302 | * 'size' => int 8099883 303 | */ 304 | public function getPublishResource($public_key, $limit = 20, $offset = 0) 305 | { 306 | if (!is_string($public_key)) { 307 | throw new \InvalidArgumentException('Публичный ключ ресурса должен быть строкового типа.'); 308 | } 309 | 310 | return (new Disk\Resource\Opened($public_key, $this, $this->uri)) 311 | ->setLimit($limit, $offset); 312 | } 313 | 314 | /** 315 | * Получение списка опубликованных файлов и папок 316 | * 317 | * @param int $limit 318 | * @param int $offset 319 | * 320 | * @return \Arhitector\Yandex\Disk\Resource\Collection 321 | */ 322 | public function getPublishResources($limit = 20, $offset = 0) 323 | { 324 | $callback = function ($parameters) { 325 | $previous = $this->setAccessTokenRequired(true); 326 | $response = $this->send( 327 | new Request( 328 | $this->uri 329 | ->withPath($this->uri->getPath() . 'resources/public') 330 | ->withQuery(http_build_query($parameters, '', '&')), 331 | 'GET' 332 | ) 333 | ); 334 | $this->setAccessTokenRequired($previous); 335 | 336 | if ($response->getStatusCode() == 200) { 337 | $response = json_decode($response->getBody(), true); 338 | 339 | if (isset($response['items'])) { 340 | return array_map(function ($item) { 341 | return new Disk\Resource\Opened($item, $this, $this->uri); 342 | }, $response['items']); 343 | } 344 | } 345 | 346 | return []; 347 | }; 348 | 349 | return (new $this->collectionClass($callback))->setLimit($limit, $offset); 350 | } 351 | 352 | /** 353 | * Ресурсы в корзине. 354 | * 355 | * @param string $path путь к файлу в корзине 356 | * @param int $limit 357 | * @param int $offset 358 | * 359 | * @return \Arhitector\Yandex\Disk\Resource\Removed 360 | * @example 361 | * 362 | * $disk->getTrashResource('file.ext') -> toArray() // файл в корзине 363 | * $disk->getTrashResource('trash:/file.ext') -> delete() 364 | */ 365 | public function getTrashResource($path, $limit = 20, $offset = 0) 366 | { 367 | if (!is_string($path)) { 368 | throw new \InvalidArgumentException('Ресурс, должен быть строкового типа - путь к файлу/папке, либо NULL'); 369 | } 370 | 371 | if (stripos($path, 'trash:/') === 0) { 372 | $path = substr($path, 7); 373 | } 374 | 375 | return (new Disk\Resource\Removed('trash:/' . ltrim($path, ' /'), $this, $this->uri)) 376 | ->setLimit($limit, $offset); 377 | } 378 | 379 | /** 380 | * Содержимое всей корзины. 381 | * 382 | * @param int $limit 383 | * @param int $offset 384 | * 385 | * @return \Arhitector\Yandex\Disk\Resource\Collection 386 | */ 387 | public function getTrashResources($limit = 20, $offset = 0) 388 | { 389 | $callback = function ($parameters) { 390 | if ( 391 | !empty($parameters['sort']) 392 | && !in_array($parameters['sort'], ['deleted', 'created', '-deleted', '-created'], true) 393 | ) { 394 | throw new \UnexpectedValueException('Допустимые значения сортировки - deleted, created и со знаком "минус".'); 395 | } 396 | 397 | $response = $this->send( 398 | new Request( 399 | $this->uri 400 | ->withPath($this->uri->getPath() . 'trash/resources') 401 | ->withQuery(http_build_query($parameters + ['path' => 'trash:/'], '', '&')), 402 | 'GET' 403 | ) 404 | ); 405 | 406 | if ($response->getStatusCode() == 200) { 407 | $response = json_decode($response->getBody(), true); 408 | 409 | if (isset($response['_embedded']['items'])) { 410 | return array_map(function ($item) { 411 | return new Disk\Resource\Removed($item, $this, $this->uri); 412 | }, $response['_embedded']['items']); 413 | } 414 | } 415 | 416 | return []; 417 | }; 418 | 419 | return (new $this->collectionClass($callback))->setSort('created')->setLimit($limit, $offset); 420 | } 421 | 422 | /** 423 | * Очистить корзину. 424 | * 425 | * @return bool|\Arhitector\Yandex\Disk\Operation 426 | */ 427 | public function cleanTrash() 428 | { 429 | $response = $this->send(new Request($this->uri->withPath($this->uri->getPath() . 'trash/resources'), 'DELETE')); 430 | 431 | if ($response->getStatusCode() == 204) { 432 | $response = json_decode($response->getBody(), true); 433 | 434 | if (!empty($response['operation'])) { 435 | return $response['operation']; 436 | } 437 | 438 | return true; 439 | } 440 | 441 | return false; 442 | } 443 | 444 | /** 445 | * Последние загруженные файлы 446 | * 447 | * @param int $limit 448 | * @param int $offset 449 | * 450 | * @return \Arhitector\Yandex\Disk\Resource\Collection 451 | * 452 | * @example 453 | * 454 | * $disk->uploaded(limit, offset) // коллекия закрытых ресурсов 455 | */ 456 | public function uploaded($limit = 20, $offset = 0) 457 | { 458 | $callback = function ($parameters) { 459 | $response = $this->send( 460 | new Request( 461 | $this->uri 462 | ->withPath($this->uri->getPath() . 'resources/last-uploaded') 463 | ->withQuery(http_build_query($parameters, '', '&')), 464 | 'GET' 465 | ) 466 | ); 467 | 468 | if ($response->getStatusCode() == 200) { 469 | $response = json_decode($response->getBody(), true); 470 | 471 | if (isset($response['items'])) { 472 | return array_map(function ($item) { 473 | return new Disk\Resource\Closed($item, $this, $this->uri); 474 | }, $response['items']); 475 | } 476 | } 477 | 478 | return []; 479 | }; 480 | 481 | return (new $this->collectionClass($callback))->setLimit($limit, $offset); 482 | } 483 | 484 | /** 485 | * Получить статус операции. 486 | * 487 | * @param string $identifier идентификатор операции или NULL 488 | * 489 | * @return \Arhitector\Yandex\Disk\Operation 490 | * 491 | * @example 492 | * 493 | * $disk->getOperation('identifier operation') 494 | */ 495 | public function getOperation($identifier) 496 | { 497 | return new Disk\Operation($identifier, $this, $this->getUri()); 498 | } 499 | 500 | /** 501 | * Возвращает количество асинхронных операций экземпляра. 502 | * 503 | * @return int 504 | */ 505 | #[\ReturnTypeWillChange] 506 | public function count() 507 | { 508 | return sizeof($this->getOperations()); 509 | } 510 | 511 | /** 512 | * Получить все операции, полученные во время выполнения сценария 513 | * 514 | * @return array 515 | * 516 | * @example 517 | * 518 | * $disk->getOperations() 519 | * 520 | * array (size=124) 521 | * 0 => 'identifier_1', 522 | * 1 => 'identifier_2', 523 | * 2 => 'identifier_3', 524 | */ 525 | public function getOperations() 526 | { 527 | return $this->operations; 528 | } 529 | 530 | /** 531 | * Отправляет запрос. 532 | * 533 | * @param \Psr\Http\Message\RequestInterface $request 534 | * 535 | * @return \Psr\Http\Message\ResponseInterface 536 | */ 537 | public function send(RequestInterface $request) 538 | { 539 | $response = parent::send($request); 540 | 541 | if ($response->getStatusCode() == 202) { 542 | if (($responseBody = json_decode($response->getBody(), true)) && isset($responseBody['href'])) { 543 | $operation = new Uri($responseBody['href']); 544 | 545 | if (!$operation->getQuery()) { 546 | $responseBody['operation'] = substr(strrchr($operation->getPath(), '/'), 1); 547 | $stream = new Stream('php://temp', 'w'); 548 | $stream->write(json_encode($responseBody)); 549 | $this->addOperation($responseBody['operation']); 550 | 551 | return $response->withBody($stream); 552 | } 553 | } 554 | } 555 | 556 | return $response; 557 | } 558 | 559 | /** 560 | * Этот экземпляр используется в качестве обёртки 561 | * 562 | * @return boolean 563 | */ 564 | public function isWrapper() 565 | { 566 | //return in_array(\Mackey\Yandex\Disk\Stream\Wrapper::SCHEME, stream_get_wrappers()); 567 | return false; 568 | } 569 | 570 | /** 571 | * Добавляет идентификатор операции в список. 572 | * 573 | * @param $identifier 574 | * 575 | * @return \Arhitector\Yandex\Disk 576 | */ 577 | protected function addOperation($identifier) 578 | { 579 | $this->operations[] = $identifier; 580 | 581 | return $this; 582 | } 583 | } 584 | -------------------------------------------------------------------------------- /src/Disk/Resource/Closed.php: -------------------------------------------------------------------------------- 1 | setContents($resource); 63 | $this->store['docviewer'] = $this->createDocViewerUrl(); 64 | $resource = $resource['path']; 65 | } 66 | 67 | if (!is_scalar($resource)) { 68 | throw new \InvalidArgumentException('Параметр "path" должен быть строкового типа.'); 69 | } 70 | 71 | $this->path = (string) $resource; 72 | $this->client = $parent; 73 | $this->uri = $uri; 74 | } 75 | 76 | /** 77 | * Получает информацию о ресурсе 78 | * 79 | * @param array $allowed выбрать ключи 80 | * 81 | * @return mixed 82 | * 83 | * @TODO добавить clearModify(), тем самым сделать возможность получать списки ресурсов во вложенных папках. 84 | */ 85 | public function toArray(array $allowed = null) 86 | { 87 | if (!$this->_toArray() || $this->isModified()) { 88 | $response = $this->client->send(new Request($this->uri->withPath($this->uri->getPath() . 'resources') 89 | ->withQuery(http_build_query(array_merge($this->getParameters($this->parametersAllowed), [ 90 | 'path' => $this->getPath() 91 | ]), '', '&')), 'GET')); 92 | 93 | if ($response->getStatusCode() == 200) { 94 | $response = json_decode($response->getBody(), true); 95 | 96 | if (!empty($response)) { 97 | $this->isModified = false; 98 | 99 | if (isset($response['_embedded'])) { 100 | $response = array_merge($response, $response['_embedded']); 101 | } 102 | 103 | unset($response['_links'], $response['_embedded']); 104 | 105 | if (isset($response['items'])) { 106 | $response['items'] = new Container\Collection(array_map(function ($item) { 107 | return new self($item, $this->client, $this->uri); 108 | }, $response['items'])); 109 | } 110 | 111 | $this->setContents($response); 112 | $this->store['docviewer'] = $this->createDocViewerUrl(); 113 | } 114 | } 115 | } 116 | 117 | return $this->_toArray($allowed); 118 | } 119 | 120 | /** 121 | * Позводляет получить метаинформацию из custom_properties 122 | * 123 | * @param string $index 124 | * @param mixed $default 125 | * 126 | * @return mixed|null 127 | */ 128 | public function getProperty($index, $default = null) 129 | { 130 | $properties = $this->get('custom_properties', []); 131 | 132 | if (isset($properties[$index])) { 133 | return $properties[$index]; 134 | } 135 | 136 | if ($default instanceof \Closure) { 137 | return $default($this); 138 | } 139 | 140 | return $default; 141 | } 142 | 143 | /** 144 | * Получает всю метаинформацию и custom_properties 145 | * 146 | * @return array 147 | */ 148 | public function getProperties() 149 | { 150 | return $this->get('custom_properties', []); 151 | } 152 | 153 | /** 154 | * Добавление метаинформации для ресурса 155 | * 156 | * @param mixed $meta строка либо массив значений 157 | * @param mixed $value NULL чтобы удалить определённую метаинформаию когда $meta строка 158 | * 159 | * @return Closed 160 | * @throws \LengthException 161 | */ 162 | public function set($meta, $value = null) 163 | { 164 | if (!is_array($meta)) { 165 | if (!is_scalar($meta)) { 166 | throw new \InvalidArgumentException('Индекс метаинформации должен быть простого типа.'); 167 | } 168 | 169 | $meta = [(string) $meta => $value]; 170 | } 171 | 172 | if (empty($meta)) { 173 | throw new \OutOfBoundsException('Не было передано ни одного значения для добавления метаинформации.'); 174 | } 175 | 176 | /*if (mb_strlen(json_encode($meta, JSON_UNESCAPED_UNICODE), 'UTF-8') > 1024) 177 | { 178 | throw new \LengthException('Максимальный допустимый размер объекта метаинформации составляет 1024 байт.'); 179 | }*/ 180 | 181 | $request = (new Request($this->uri->withPath($this->uri->getPath() . 'resources') 182 | ->withQuery(http_build_query(['path' => $this->getPath()], '', '&')), 'PATCH')); 183 | 184 | $request->getBody() 185 | ->write(json_encode(['custom_properties' => $meta])); 186 | 187 | $response = $this->client->send($request); 188 | 189 | if ($response->getStatusCode() == 200) { 190 | $this->setContents(json_decode($response->getBody(), true)); 191 | $this->store['docviewer'] = $this->createDocViewerUrl(); 192 | } 193 | 194 | return $this; 195 | } 196 | 197 | /** 198 | * Разрешает обновление свойств объекта как массива 199 | * 200 | * @param string $key 201 | * @param mixed $value 202 | * 203 | * @return void 204 | */ 205 | #[\ReturnTypeWillChange] 206 | public function offsetSet($key, $value) 207 | { 208 | $this->set($key, $value); 209 | } 210 | 211 | /** 212 | * Магический метод set. Добавляет метаинформацию 213 | * 214 | * @return void 215 | */ 216 | public function __set($key, $value) 217 | { 218 | $this->set($key, $value); 219 | } 220 | 221 | /** 222 | * Разрешает использование unset() к метаинформации 223 | * 224 | * @param string $key 225 | * 226 | * @return void 227 | * @throws \RuntimeException 228 | */ 229 | #[\ReturnTypeWillChange] 230 | public function offsetUnset($key) 231 | { 232 | $this->set($key, null); 233 | } 234 | 235 | /** 236 | * Магический метод unset. Удаляет метаинформацию. 237 | * 238 | * @return void 239 | */ 240 | public function __unset($key) 241 | { 242 | $this->set($key, null); 243 | } 244 | 245 | /** 246 | * Удаление файла или папки 247 | * 248 | * @param boolean $permanently TRUE Признак безвозвратного удаления 249 | * 250 | * @return bool|\Arhitector\Yandex\Disk\Operation|\Arhitector\Yandex\Disk\Resource\Removed 251 | */ 252 | public function delete($permanently = false) 253 | { 254 | $response = $this->client->send(new Request($this->uri->withPath($this->uri->getPath() . 'resources') 255 | ->withQuery(http_build_query([ 256 | 'path' => $this->getPath(), 257 | 'permanently' => (bool) $permanently 258 | ])), 'DELETE')); 259 | 260 | if ($response->getStatusCode() == 202 || $response->getStatusCode() == 204) { 261 | $this->setContents([]); 262 | 263 | $this->emit('delete', $this, $this->client); 264 | $this->client->emit('delete', $this, $this->client); 265 | 266 | if ($response->getStatusCode() == 202) { 267 | $response = json_decode($response->getBody(), true); 268 | 269 | if (isset($response['operation'])) { 270 | $response['operation'] = $this->client->getOperation($response['operation']); 271 | $this->emit('operation', $response['operation'], $this, $this->client); 272 | $this->client->emit('operation', $response['operation'], $this, $this->client); 273 | 274 | return $response['operation']; 275 | } 276 | } 277 | 278 | try { 279 | /*$resource = $this->client->getTrashResource('/', 0); 280 | $resource = $this->client->getTrashResources(1, $resource->get('total', 0) - 1)->getFirst(); 281 | 282 | if ($resource->has() && $resource->get('origin_path') == $this->getPath()) 283 | { 284 | return $resource; 285 | }*/ 286 | } catch (\Exception $exc) { 287 | } 288 | 289 | return true; 290 | } 291 | 292 | return false; 293 | } 294 | 295 | /** 296 | * Перемещение файла или папки. 297 | * Перемещать файлы и папки на Диске можно, указывая текущий путь к ресурсу и его новое положение. 298 | * Если запрос был обработан без ошибок, API составляет тело ответа в зависимости от вида указанного ресурса – 299 | * ответ для пустой папки или файла отличается от ответа для непустой папки. (Если запрос вызвал ошибку, 300 | * возвращается подходящий код ответа, а тело ответа содержит описание ошибки). 301 | * Приложения должны самостоятельно следить за статусами запрошенных операций. 302 | * 303 | * @param string|\Arhitector\Yandex\Disk\Resource\Closed $destination новый путь. 304 | * @param boolean $overwrite признак перезаписи файлов. Учитывается, 305 | * если ресурс перемещается в папку, в которой 306 | * уже есть ресурс с таким именем. 307 | * 308 | * @return bool|\Arhitector\Yandex\Disk\Operation 309 | */ 310 | public function move($destination, $overwrite = false) 311 | { 312 | if ($destination instanceof Closed) { 313 | $destination = $destination->getPath(); 314 | } 315 | 316 | $response = $this->client->send(new Request($this->uri->withPath($this->uri->getPath() . 'resources/move') 317 | ->withQuery(http_build_query([ 318 | 'from' => $this->getPath(), 319 | 'path' => $destination, 320 | 'overwrite' => (bool) $overwrite 321 | ], '', '&')), 'POST')); 322 | 323 | if ($response->getStatusCode() == 202 || $response->getStatusCode() == 201) { 324 | $this->path = $destination; 325 | $response = json_decode($response->getBody(), true); 326 | 327 | if (isset($response['operation'])) { 328 | $response['operation'] = $this->client->getOperation($response['operation']); 329 | $this->emit('operation', $response['operation'], $this, $this->client); 330 | $this->client->emit('operation', $response['operation'], $this, $this->client); 331 | 332 | return $response['operation']; 333 | } 334 | 335 | return true; 336 | } 337 | 338 | return false; 339 | } 340 | 341 | /** 342 | * Создание папки, если ресурса с таким же именем нет 343 | * 344 | * @return \Arhitector\Yandex\Disk\Resource\Closed 345 | * @throws mixed 346 | */ 347 | public function create() 348 | { 349 | try { 350 | $this->client->send(new Request($this->uri->withPath($this->uri->getPath() . 'resources') 351 | ->withQuery(http_build_query([ 352 | 'path' => $this->getPath() 353 | ], '', '&')), 'PUT')); 354 | $this->setContents([]); 355 | } catch (\Exception $exc) { 356 | throw $exc; 357 | } 358 | 359 | return $this; 360 | } 361 | 362 | /** 363 | * Публикация ресурса\Закрытие доступа 364 | * 365 | * @param boolean $publish TRUE открыть доступ, FALSE закрыть доступ 366 | * 367 | * @return \Arhitector\Yandex\Disk\Resource\Closed|\Arhitector\Yandex\Disk\Resource\Opened 368 | */ 369 | public function setPublish($publish = true) 370 | { 371 | $request = 'resources/unpublish'; 372 | 373 | if ($publish) { 374 | $request = 'resources/publish'; 375 | } 376 | 377 | $response = $this->client->send(new Request($this->uri->withPath($this->uri->getPath() . $request) 378 | ->withQuery(http_build_query([ 379 | 'path' => $this->getPath() 380 | ], '', '&')), 'PUT')); 381 | 382 | if ($response->getStatusCode() == 200) { 383 | $this->setContents([]); 384 | 385 | if ($publish && $this->has('public_key')) { 386 | return $this->client->getPublishResource($this->get('public_key')); 387 | } 388 | } 389 | 390 | return $this; 391 | } 392 | 393 | /** 394 | * Скачивает файл 395 | * 396 | * @param resource|StreamInterface|string $destination Путь, по которому будет сохранён файл 397 | * StreamInterface будет записан в поток 398 | * resource открытый на запись 399 | * @param mixed $overwrite 400 | * 401 | * @return bool 402 | * 403 | * @throws NotFoundException 404 | * @throws AlreadyExistsException 405 | * @throws \OutOfBoundsException 406 | * @throws \UnexpectedValueException 407 | */ 408 | public function download($destination, $overwrite = false) 409 | { 410 | $destination_type = gettype($destination); 411 | 412 | if (!$this->has()) { 413 | throw new NotFoundException('Не удалось найти запрошенный ресурс.'); 414 | } 415 | 416 | if (is_resource($destination)) { 417 | $destination = new Stream($destination); 418 | } 419 | 420 | if ($destination instanceof StreamInterface) { 421 | if (!$destination->isWritable()) { 422 | throw new \OutOfBoundsException('Дескриптор файла должен быть открыт с правами на запись.'); 423 | } 424 | } else { 425 | if ($destination_type == 'string') { 426 | if (is_file($destination) && !$overwrite) { 427 | throw new AlreadyExistsException('По указанному пути "' . $destination . '" уже существует ресурс.'); 428 | } 429 | 430 | if (!is_writable(dirname($destination))) { 431 | throw new \OutOfBoundsException('Запрещена запись в директорию, в которой должен быть расположен файл.'); 432 | } 433 | 434 | $destination = new Stream($destination, 'w+b'); 435 | } else { 436 | throw new \InvalidArgumentException('Такой тип параметра $destination не поддерживается.'); 437 | } 438 | } 439 | 440 | $response = $this->client->send(new Request($this->uri->withPath($this->uri->getPath() . 'resources/download') 441 | ->withQuery(http_build_query(['path' => $this->getPath()], '', '&')), 'GET')); 442 | 443 | if ($response->getStatusCode() == 200) { 444 | $response = json_decode($response->getBody(), true); 445 | 446 | if (isset($response['href'])) { 447 | $response = $this->client->send(new Request($response['href'], 'GET')); 448 | 449 | if ($response->getStatusCode() == 200) { 450 | $stream = $response->getBody(); 451 | 452 | while (!$stream->eof()) { 453 | $destination->write($stream->read(16384)); 454 | } 455 | 456 | $stream->close(); 457 | $this->emit('downloaded', $this, $destination, $this->client); 458 | $this->client->emit('downloaded', $this, $destination, $this->client); 459 | 460 | if ($destination_type == 'object') { 461 | return $destination; 462 | } 463 | 464 | return $destination->getSize(); 465 | } 466 | 467 | return false; 468 | } 469 | } 470 | 471 | throw new \UnexpectedValueException('Не удалось запросить разрешение на скачивание, повторите заново.'); 472 | } 473 | 474 | /** 475 | * Копирование файла или папки 476 | * 477 | * @param string|Closed $destination 478 | * @param bool $overwrite 479 | * 480 | * @return bool 481 | */ 482 | public function copy($destination, $overwrite = false) 483 | { 484 | if ($destination instanceof Closed) { 485 | $destination = $destination->getPath(); 486 | } 487 | 488 | $response = $this->client->send(new Request($this->uri->withPath($this->uri->getPath() . 'resources/copy') 489 | ->withQuery(http_build_query([ 490 | 'from' => $this->getPath(), 491 | 'path' => $destination, 492 | 'overwrite' => (bool) $overwrite 493 | ], '', '&')), 'POST')); 494 | 495 | if ($response->getStatusCode() == 201) { 496 | $response = json_decode($response->getBody(), true); 497 | 498 | if (isset($response['operation'])) { 499 | $response['operation'] = $this->client->getOperation($response['operation']); 500 | $this->emit('operation', $response['operation'], $this, $this->client); 501 | $this->client->emit('operation', $response['operation'], $this, $this->client); 502 | 503 | return $response['operation']; 504 | } 505 | 506 | return true; 507 | } 508 | 509 | return false; 510 | } 511 | 512 | /** 513 | * Загрузить файл на диск 514 | * 515 | * @param mixed $file_path может быть как путь к локальному файлу, так и URL к файлу. 516 | * @param bool $overwrite если ресурс существует на Яндекс.Диске TRUE перезапишет ресурс. 517 | * @param bool $disable_redirects помогает запретить редиректы по адресу, TRUE запретит пере адресацию. 518 | * 519 | * @return bool|\Arhitector\Yandex\Disk\Operation 520 | * 521 | * @throws mixed 522 | * 523 | * @TODO Добавить, если передана папка - сжать папку в архив и загрузить. 524 | */ 525 | public function upload($file_path, $overwrite = false, $disable_redirects = false) 526 | { 527 | if (is_string($file_path)) { 528 | $scheme = substr($file_path, 0, 7); 529 | 530 | if ($scheme == 'http://' or $scheme == 'https:/') { 531 | try { 532 | $response = $this->client->send(new Request($this->uri->withPath($this->uri->getPath() . 'resources/upload') 533 | ->withQuery(http_build_query([ 534 | 'url' => $file_path, 535 | 'path' => $this->getPath(), 536 | 'disable_redirects' => (int) $disable_redirects 537 | ], '', '&')), 'POST')); 538 | } catch (AlreadyExistsException $exc) { 539 | // параметр $overwrite не работает т.к. диск не поддерживает {AlreadyExistsException:409}->rename->delete 540 | throw new AlreadyExistsException( 541 | $exc->getMessage() . ' Перезапись для удалённой загрузки не доступна.', 542 | $exc->getCode(), 543 | $exc 544 | ); 545 | } 546 | 547 | $response = json_decode($response->getBody(), true); 548 | 549 | if (isset($response['operation'])) { 550 | $response['operation'] = $this->client->getOperation($response['operation']); 551 | $this->emit('operation', $response['operation'], $this, $this->client); 552 | $this->client->emit('operation', $response['operation'], $this, $this->client); 553 | 554 | return $response['operation']; 555 | } 556 | 557 | return false; 558 | } 559 | 560 | $file_path = realpath($file_path); 561 | 562 | if (!is_file($file_path)) { 563 | throw new \OutOfBoundsException('Локальный файл по такому пути: "' . $file_path . '" отсутствует.'); 564 | } 565 | } else { 566 | if (!is_resource($file_path)) { 567 | throw new \InvalidArgumentException('Параметр "путь к файлу" должен быть строкового типа или открытый файловый дескриптор на чтение.'); 568 | } 569 | } 570 | 571 | $access_upload = json_decode($this->client->send(new Request($this->uri->withPath($this->uri->getPath() . 'resources/upload') 572 | ->withQuery(http_build_query([ 573 | 'path' => $this->getPath(), 574 | 'overwrite' => (int) ((bool) $overwrite), 575 | ], '', '&')), 'GET')) 576 | ->getBody(), true); 577 | 578 | if (!isset($access_upload['href'])) { 579 | // $this->client->setRetries = 1 580 | throw new \RuntimeException('Не возможно загрузить локальный файл - не получено разрешение.'); 581 | } 582 | 583 | if ($this->getEmitter()->hasListeners('progress')) { 584 | $stream = new Progress($file_path, 'rb'); 585 | $stream->addListener('progress', function (Event $event, $percent) { 586 | $this->emit('progress', $percent); 587 | }); 588 | } else { 589 | $stream = new Stream($file_path, 'rb'); 590 | } 591 | 592 | $response = $this->client->send((new Request($access_upload['href'], 'PUT', $stream))); 593 | $this->emit('uploaded', $this, $this->client, $stream, $response); 594 | $this->client->emit('uploaded', $this, $this->client, $stream, $response); 595 | 596 | return $response->getStatusCode() == 201; 597 | } 598 | 599 | /** 600 | * Получает прямую ссылку 601 | * 602 | * @return string 603 | * @throws mixed 604 | * 605 | * @TODO Не работает для файлов вложенных в публичную папку. 606 | */ 607 | public function getLink() 608 | { 609 | if (!$this->has()) { 610 | throw new NotFoundException('Не удалось найти запрошенный ресурс.'); 611 | } 612 | 613 | $response = $this->client->send(new Request($this->uri->withPath($this->uri->getPath() . 'resources/download') 614 | ->withQuery(http_build_query([ 615 | 'path' => (string) $this->getPath() 616 | ], '', '&')), 'GET')); 617 | 618 | if ($response->getStatusCode() == 200) { 619 | $response = json_decode($response->getBody(), true); 620 | 621 | if (isset($response['href'])) { 622 | return $response['href']; 623 | } 624 | } 625 | 626 | throw new \UnexpectedValueException('Не удалось запросить разрешение на скачивание, повторите заново'); 627 | } 628 | 629 | /** 630 | * Получает ссылку для просмотра документа. Достпно владельцу аккаунта. 631 | * 632 | * @return bool|string 633 | */ 634 | protected function createDocViewerUrl() 635 | { 636 | if ($this->isFile()) { 637 | $docviewer = [ 638 | 'url' => $this->get('path'), 639 | 'name' => $this->get('name') 640 | ]; 641 | 642 | if (strpos($docviewer['url'], 'disk:/') === 0) { 643 | $docviewer['url'] = substr($docviewer['url'], 6); 644 | } 645 | 646 | $docviewer['url'] = "ya-disk:///disk/{$docviewer['url']}"; 647 | 648 | return (string) (new Uri('https://docviewer.yandex.ru')) 649 | ->withQuery(http_build_query($docviewer, '', '&')); 650 | } 651 | 652 | return false; 653 | } 654 | } 655 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Введение 3 | 4 | Неофициальное PHP SDK для некоторых сервисов Яндекса: сервис Яндекс.Диск. 5 | 6 | ## Список изменений 7 | 8 | 21/11/2022 9 | 10 | - PHP 7.4, 8.0, 8.1 11 | 12 | 21/12/2020 13 | 14 | - Начиная с 2.1 минимальная версия php 7.3 15 | - zend-diactoros заменён на laminas-diactoros (laminas-zendframework-bridge) 16 | 17 | 23/08/2016 18 | 19 | - метод `upload` поддерживает событие `progress`, слушатель принимает `float` значение в процентах. 20 | - возвращена ранее удалённая опция `disable_redirects`. 21 | - исправление грамматических ошибок в `README.md` 22 | 23 | ## Требования 24 | 25 | - PHP >= 5.6 26 | - Расширение php_curl 27 | 28 | ## Внести свой вклад в развитие 29 | 30 | Вы можете сообщить о найденных неточностях в работе SDK, приветствуется помощь в разработке. Чтобы начать помогать вести разработку Вам нужно создать fork репозитория ветки **development**, внесите изменения в код и отправьте pull request нам с изменениями в ветку **development**. 31 | 32 | ## Установка 33 | 34 | Поддерживается установка с помощью [менеджера пакетов](https://getcomposer.org). 35 | 36 | ``` 37 | $ composer require arhitector/yandex dev-master 38 | ``` 39 | 40 | Или 41 | 42 | ``` 43 | $ php composer.phar require arhitector/yandex dev-master 44 | ``` 45 | 46 | ## Тесты 47 | 48 | Вы можете не найти некоторых тестов - мы их не публикуем по причинам приватности. 49 | 50 | ``` 51 | $ composer test 52 | ``` 53 | 54 | ## 1. Сервис Яндекс.Диск 55 | ### 1.1. Введение 56 | 57 | PHP SDK для работы с Яндекс.Диском, в своей основе использует REST API диска. API диска для аунтификации использует OAuth-токен (например, *0c4181a7c2cf4521964a72ff57a34a07*), который Вы должны получить самостоятельно: 58 | - зарегистрировать приложение и самостоятельно получить токен https://oauth.yandex.ru 59 | - или воспользоваться возможностями SDK, читайте о методе *AccessToken::refreshAccessToken* (RFC 6749#4.3.2. в скором времени, а возможно уже, будет отключен) 60 | 61 | SDK работает только с отладочными токенами. OAuth-токен должен иметь разрешённые права "**Яндекс.Диск REST API**". 62 | 63 | **Ресурс** - файл или папка на Яндекс.Диске. SDK определяет три состояния ресурса: **публичный**, **закрытый**, и тот который **помещён в корзину**. 64 | 65 | ### 1.1.1. Возможности 66 | 67 | Основные моменты 68 | 69 | - Работа с файлами на Яндекс.Диске (получение информации, копирование, перемещение, загрузка, скачивание и т.д.) 70 | - Работа с публичными ресурсами (публикация, скачивание, копирование на свой Яндекс.Диск, и т.п.) 71 | - Работа с ресурсами в корзине (список файлов в корзине, очистка корзины, восстановление файла из корзины и прочее) 72 | - Поддерживает события: 'operation', 'downloaded', 'uploaded', 'deleted' 73 | - ~~Шифрование файлов (не поддерживается, используйте ветку 1.0)~~ 74 | - Получение ссылок DocViewer 75 | 76 | возможно это не полный список 77 | 78 | ### Плагины, дополнения, адаптеры 79 | 80 | - адаптер [yandex-disk-flysystem](https://github.com/jack-theripper/yandex-disk-flysystem) для [thephpleague/flysystem](https://github.com/thephpleague/flysystem) (существующий адаптер WebDav хорош, но не позволяет публиковать ресурсы - тут эта возможность имеется). 81 | 82 | ## 1.1.2. Папки приложений 83 | 84 | Приложения могут хранить на Диске пользователя собственные данные — например, настройки, сделанные этим пользователем или созданные им файлы. 85 | Чтобы запрашивать доступ к собственной папке на Диске, приложение следует зарегистрировать с правом "**Доступ к папке приложения на Диске**". 86 | Такое приложение сможет оперировать файлами только в рамках своей папки, если не получит также прав на общий доступ к Диску. 87 | SDK различает общий доступ и доступ приложения к собственной папке, префиксами "**disk:/**" и "**app:/**" соответственно. 88 | Тем не менее в информации о ресурсе пути указываются в схеме disk:/, с абсолютными путями к ресурсам, например "*disk:/Приложения/МоёПервоеПриложение/photo.png*". 89 | 90 | ## 1.1.3. Пример использования 91 | 92 | ```php 93 | 94 | // передать OAuth-токен зарегистрированного приложения. 95 | $disk = new Arhitector\Yandex\Disk('OAuth-токен'); 96 | 97 | /** 98 | * Получить Объектно Ориентированное представление закрытого ресурса. 99 | * @var Arhitector\Yandex\Disk\Resource\Closed $resource 100 | */ 101 | $resource = $disk->getResource('новый файл.txt'); 102 | 103 | // проверить сущестует такой файл на диске ? 104 | $resource->has(); // вернет, например, false 105 | 106 | // загрузить файл на диск под имененм "новый файл.txt". 107 | $resource->upload(__DIR__.'/файл в локальной папке.txt'); 108 | 109 | // файл загружен, вывести информацию. 110 | var_dump($resource->toArray()) 111 | 112 | // теперь удалить в корзину. 113 | $removed = $resource->delete(); 114 | ``` 115 | 116 | SDK вызывает исключения по каждой ситуации, следующий пример показывает как это можно использовать. 117 | 118 | ```php 119 | try 120 | { 121 | try 122 | { 123 | /** 124 | * Получить закрытый ресурс 125 | * @var Arhitector\Yandex\Disk\Resource\Closed $resource 126 | */ 127 | $resource = $disk->getResource('новый файл.txt'); 128 | 129 | // До этого момента запросов к Диску не было 130 | // Только сейчас был сделан запрос на получение информации о ресурсе к Яндекс.Диску 131 | // Вывести информацию. Когда ресурс не найден будет вызвано исключение NotFoundException 132 | $resource->toArray(); 133 | } 134 | catch (Arhitector\Yandex\Client\Exception\NotFoundException $exc) 135 | { 136 | // Ресурс на Диске отсутствует, загрузить под именем 'новый файл.txt' 137 | $resource->upload(__DIR__.'/файл в локальной папке.txt'); 138 | } 139 | 140 | // Теперь удалю, совсем. 141 | $file->delete(true); 142 | } 143 | catch (Arhitector\Yandex\Client\Exception\UnauthorizedException $exc) 144 | { 145 | // Записать в лог, авторизоваться не удалось 146 | log($exc->getMessage()); 147 | } 148 | catch (Exception $exc) 149 | { 150 | // Что-то другое 151 | } 152 | ``` 153 | 154 | ## 1.2. Как подключиться к Яндекс.Диску 155 | 156 | Обращение к Яндекс.Диску осуществляется через **Arhitector\Yandex\Disk**. После получения OAuth-токена, его (OAuth-токен) можно использовать следующим образом: 157 | 158 | - Вариант 1. Инициализировать клиент. 159 | 160 | Один и тот же OAuth-токен может быть использован для доступа к разным сервисам. 161 | 162 | ```php 163 | $client = new Arhitector\Yandex\Client\OAuth('OAuth-токен'); 164 | ``` 165 | 166 | Инициализировать клиент Яндекс.Диска и передать `$client`. 167 | 168 | ```php 169 | $disk = new Arhitector\Yandex\Disk($client); 170 | ``` 171 | 172 | - Вариант 2. Инициализировать клиент Яндекс.Диска с передачей OAuth-токена в конструктор. 173 | 174 | ```php 175 | $disk = new Arhitector\Yandex\Disk('OAuth-токен'); 176 | ``` 177 | 178 | - Вариант 3. Инициализировать клиент Яндекс.Диска без передачи OAuth-токена. 179 | 180 | Вы можете установить или изменить OAuth-токен в ранее инициализированном объекте. 181 | 182 | ```php 183 | $client->setAccessToken('OAuth-токен'); 184 | ``` 185 | 186 | Или изменить OAuth-токен для клиента Яндекс.Диска 187 | 188 | ```php 189 | $disk->setAccessToken('OAuth-токен'); 190 | ``` 191 | 192 | На этом этапе есть несколько переменных, например `$disk` и `$client`, которые будут использованы далее в документации для отсылки к определённым объектам. 193 | 194 | ```php 195 | /** 196 | * @var Arhitector\Yandex\Client\OAuth $client 197 | * @var Arhitector\Yandex\Disk $disk 198 | */ 199 | ``` 200 | 201 | > Примечание: Arhitector\Yandex\Client\OAuth не является реализацией протокола OAuth 2.0. 202 | 203 | ### 1.2.1. Установить OAuth-токен 204 | 205 | Устанавливает OAuth-токен для прохождения аунтификации на сервисах. Не все операции требуют OAuth-токен. 206 | 207 | ```php 208 | public $this OAuth::setAccessToken(string $token) 209 | 210 | public $this Disk::setAccessToken(string $token) 211 | ``` 212 | 213 | **Примеры** 214 | 215 | ```php 216 | $disk->setAccessToken('0c4181a7c2cf4521964a72ff57a34a07'); 217 | ``` 218 | 219 | или 220 | 221 | ```php 222 | $client->setAccessToken('0c4181a7c2cf4521964a72ff57a34a07'); 223 | ``` 224 | 225 | ### 1.2.2. Получить установленный OAuth-токен 226 | 227 | Получает ранее установленный OAuth-токен или `NULL`. 228 | 229 | ```php 230 | public mixed OAuth::getAccessToken( void ); 231 | 232 | public mixed Disk::getAccessToken( void ); 233 | ``` 234 | 235 | **Примеры** 236 | 237 | ```php 238 | $disk->getAccessToken(); // null 239 | ``` 240 | 241 | или 242 | 243 | ```php 244 | $client->getAccessToken(); // string '0c4181a7c2cf4521964a72ff57a34a07' 245 | ``` 246 | 247 | ## 1.3. Работа с Яндекс.Диском 248 | 249 | SDK различает три типа ресурсов: **публичный**, **закрытый**, и тот который **помещён в корзину**. Каждый из типов представлен своим объектом. Для любого типа ресурса доступна фильтрация (методы **setMediaType**, **setType** и т.д.) у каждого из типов свой набор возможных значений. 250 | 251 | - Публичный ресурс, `Arhitector\Yandex\Disk\Resource\Opened` 252 | 253 | - Ресурс доступный владельцу, `Arhitector\Yandex\Disk\Resource\Closed` 254 | 255 | - Ресурс в корзине, `Arhitector\Yandex\Disk\Resource\Removed` 256 | 257 | ### Введение 258 | 259 | Существуют базовые методы получения всевозможного рода информации, которые доступны везде. Кроме прочего, поддерживаются обращения к фиктивным свойствам и работа с объектом как с массивом. 260 | 261 | - Объект->свойство 262 | 263 | ```php 264 | $disk->total_space; // объём диска 265 | 266 | $resource->size; // размер файла 267 | ``` 268 | 269 | - Объект['свойство'] 270 | 271 | ```php 272 | $disk['free_space']; // свободное место 273 | 274 | $resource['name']; // название файла/папки 275 | ``` 276 | 277 | ### 1.3.1. Метод get 278 | 279 | Получить значение по ключу. 280 | 281 | ```php 282 | public mixed Объект::get(string $index [, mixed $default = null]) 283 | ``` 284 | 285 | `$index` - индекс/ключ, по которому получить значение (`free_space`, `name` и т.д.) 286 | 287 | 288 | `$default` - значение по умолчанию, если такой индекс отсутствует - может принимать анонимную функцию, которая будет вызвана с текущим контекстом (`Disk`, `Closed`, и т.д.) 289 | 290 | **Примеры** 291 | 292 | ```php 293 | // индекс total_space 294 | $disk->get('total_space'); 295 | 296 | // custom_properties или FALSE если отсутствует 297 | $resource->get('custom_properties', false); 298 | 299 | // вернёт результат 'any thing' анонимной функции 300 | $removedResource->get('property_123', function (Removed $resource) { 301 | return 'any thing'; 302 | }); 303 | ``` 304 | 305 | ### 1.3.2. Метод toArray 306 | 307 | Получает содержимое всего контейнера в виде массива. 308 | 309 | > Примечание: метод не является рекурсивным, это означает, что вложенные ресурсы (например, файлы в папке) не будут преобразованы в массив, а результатом будет массив объектов т.е. массив ресурсов (файлы, папки), представленные своим объектом. 310 | 311 | ```php 312 | public array Объект::toArray([array $allowed = null]) 313 | ``` 314 | 315 | `$allowed` - массив ключей, которые необходимо вернуть. 316 | 317 | **Примеры** 318 | 319 | ```php 320 | // массив информация о Яндекс.Диске 321 | $disk->toArray(); 322 | 323 | // получить только 324 | $disk->toArray(['total_space', 'free_space']); 325 | 326 | // массив объектов 327 | $collection->toArray(); 328 | 329 | // массив, информация о ресурсе 330 | $resource->toArray(); 331 | ``` 332 | 333 | ### 1.3.3. Метод toObject 334 | 335 | Получает содержимое всего контейнера в виде объекта. 336 | 337 | > Примечание: метод не является рекурсивным, это означает, что вложенные ресурсы (например, файлы в папке) не будут преобразованы в объект, а результатом будет коллекция объектов. 338 | 339 | ```php 340 | public stdClass Объект::toObject([array $allowed = null]) 341 | ``` 342 | 343 | `$allowed` - получить только эти ключи. 344 | 345 | 346 | **Примеры** 347 | 348 | ```php 349 | $disk->toObject(); 350 | 351 | $collection->toObject(); 352 | 353 | $resource->toObject(['name', 'type']); 354 | ``` 355 | 356 | ### 1.3.4. Метод getIterator 357 | 358 | Получает итератор. Вы можете использовать объекты SDK в циклах. 359 | 360 | ```php 361 | public ArrayIterator Объект::getIterator( void ) 362 | ``` 363 | 364 | **Примеры** 365 | 366 | ```php 367 | $disk->getIterator(); 368 | 369 | $collection->getIterator(); 370 | 371 | $resource->items->getIterator(); 372 | ``` 373 | 374 | Проход циклом, например, `$resource` является папкой. Получим вложенные файлы/папки в эту папку. 375 | 376 | ```php 377 | foreach ($resource->items as $item) 378 | { 379 | // $item объект ресурса `Resource\\*`, вложенный в папку. 380 | } 381 | ``` 382 | 383 | ### 1.3.5. Метод count 384 | 385 | Подсчитывает количество чего-то. 386 | 387 | ```php 388 | public integer Объект::count( void ) 389 | ``` 390 | 391 | Возвращает количество асинхронных операций экземпляра: 392 | 393 | - Disk::count() 394 | 395 | Возвращает количество полей: 396 | 397 | - Resource\\*::count() 398 | 399 | **Примеры** 400 | 401 | ```php 402 | // Возвращает количество асинхронных операций экземпляра. 403 | $disk->count(); 404 | 405 | // в других случаях размер контейнера 406 | $resource->items->count(); 407 | ``` 408 | 409 | ### 1.3.6. Методы has, hasProperty 410 | 411 | #### Метод has 412 | 413 | Поведение метода `has` отличается в зависимости от контекста. Может проверить существует ли свойство или существует ли такой ресурс на Яндекс.Диске. 414 | 415 | > Примечание: возможно в будущем поведение метода будет упрощено. 416 | 417 | ```php 418 | public bool Объект::has([string $key = NULL]) 419 | ``` 420 | 421 | `$key` - необязательный параметр, индекс. 422 | 423 | - Вызов с параметром проверяет свойство на существование 424 | 425 | ```php 426 | $disk->has('total_space_123'); // false 427 | 428 | $resource->has('name'); // true 429 | ``` 430 | 431 | - Вызов без параметров поддерживается только в контексте ресурса `Resource\\*` и проверяет ресурс на существование. 432 | 433 | ```php 434 | $resource->has(); // true 435 | ``` 436 | 437 | #### Метод hasProperty 438 | 439 | Тоже самое что и метод `has`, но выполняет только одно действие - проверка свойства на существование и доступен только в контексте ресурса `Resource\\*` 440 | 441 | ```php 442 | public boolean Объект::hasProperty(string $key) 443 | ``` 444 | 445 | `$key` - индекс/свойство для проверки на существование. 446 | 447 | 448 | **Примеры** 449 | 450 | ```php 451 | $resource->hasProperty('custom_properties'); // false 452 | ``` 453 | 454 | ### 1.3.7. Получение информации о диске. 455 | 456 | Методы получения информации описаны выше. 457 | 458 | ```php 459 | $disk->toArray(); 460 | ``` 461 | 462 | Вернёт массив, примерно такого содержания. Метод `toObject` возвращает соответственно объект. 463 | 464 | ```php 465 | array (size=5) 466 | 'trash_size' => int 187017199 467 | 'total_space' => float 14495514624 468 | 'used_space' => float 14083430863 469 | 'system_folders' => array (size=2) 470 | 'applications' => string 'disk:/Приложения' (length=26) 471 | 'downloads' => string 'disk:/Загрузки/' (length=23) 472 | 'free_space' => float 412083761 473 | ``` 474 | 475 | Метод `count` тут вернёт количество инициированных асинхронных операций. 476 | 477 | ```php 478 | $disk->count(); // int 0 479 | 480 | count($disk); // int 5 481 | ``` 482 | Доступные ключи для метода `get` 483 | 484 | - trash_size - размер корзины в байтах. 485 | - total_space - объём диска в байтах. 486 | - used_space - использованное место в байтах. 487 | - free_space - свободное место в байтах. 488 | - system_folders - массив содержит пути к системным папкам. 489 | 490 | ```php 491 | // метод get 492 | $disk->get('total_space'); // float 14495514624 493 | 494 | // объект->свойство 495 | $disk->used_space; // float 14083430863 496 | 497 | // объект['свойство'] 498 | $disk['system_folders']; 499 | 500 | /* array (size=2) 501 | 'applications' => string 'disk:/Приложения' (length=26) 502 | 'downloads' => string 'disk:/Загрузки/' (length=23) */ 503 | 504 | // используем параметр $default 505 | $disk->get('не существующее свойство', 'default value'); // string 'default value' 506 | ``` 507 | 508 | ### 1.3.8. Работа с закрытыми ресурсами. 509 | 510 | Работа с ресурсами на диске осуществляется через метод ы `Disk::getResource` и `Disk::getResources`, доступ к которым имеет владелец диска. Не имеет значения существует ли ресурс на диске в данный момент или нет. Разница в том, что когда ресурс существует - есть возможность запросить информацию о ресурсе в другом случае будет вызвано исключение NotFoundException. По факту для ресурса, который еще не существует доступна только операция загрузки на диск - **upload**, после чего операции публикации, удаления и т.п. смогут корректно выполняться. 511 | 512 | #### Метод Disk::getResource 513 | 514 | Получает объектно ориентированное представление конкретного ресурса на Яндекс.Диске. Доступ к такому ресурсу имеет владелец диска. 515 | 516 | ```php 517 | public Resource\Closed Disk::getResource(string $path [, int $limit = 20 [, int $offset = 0]]) 518 | ``` 519 | 520 | `$path` - Путь к новому либо уже существующему ресурсу. 521 | 522 | 523 | `$limit` - Количество ресурсов в ответе. 524 | 525 | 526 | `$offset` - Смещение. Задаётся для списка всех файлов или если ресурс является папка, то задаёт смещение вложенных в папку ресурсов. 527 | 528 | **Примеры** 529 | 530 | Получить объект ресурса. 531 | 532 | ```php 533 | /** 534 | * @var Arhitector\Yandex\Disk\Resource\Closed $resource 535 | */ 536 | $resource = $disk->getResource('/путь от корня диска/до файла/или папки/название.txt'); 537 | 538 | $resource = $disk->getResource('disk:/путь от корня диска/до файла/или папки/название.txt'); 539 | ``` 540 | 541 | Получить объект ресурса из папки приложения. 542 | 543 | ```php 544 | /** 545 | * @var Arhitector\Yandex\Disk\Resource\Closed $resource 546 | */ 547 | $resource = $disk->getResource('app:/название.txt', 100, 10); 548 | ``` 549 | 550 | Установить `$limit` и `offset` можно и после получения объекта ресурса методами `setLimit` и `setOffset`. 551 | 552 | ```php 553 | $resource = $disk->getResource('/', 10); 554 | 555 | $resource = $disk->getResource('/', 10, 5); 556 | 557 | $resource->setLimit(100); 558 | 559 | $resource->setOffset(200); 560 | ``` 561 | 562 | #### Метод Disk::getResources, список всех файлов. 563 | 564 | Получает список всех файлов в папках, под папках и т.д. Список представлен объектом `Arhitector\Yandex\Disk\Resource\Collection`. 565 | 566 | ```php 567 | public Resource\Collection Disk::getResources([, int $limit = 20 [, int $offset = 0]]) 568 | ``` 569 | 570 | Здесь доступны методы фильтрации, основные методы получения информации и ряд других. 571 | 572 | **Примеры** 573 | 574 | ```php 575 | /** 576 | * Получить список всех файлов 577 | * 578 | * @var Disk\Resource\Collection $collection 579 | */ 580 | $collection = $disk->getResources(); 581 | 582 | $disk->getResources(100, 15); 583 | ``` 584 | 585 | Список файлов в папках также представлен объектом `Arhitector\Yandex\Disk\Resource\Collection` 586 | 587 | ```php 588 | $resource->items; // object 'Arhitector\Yandex\Disk\Resource\Collection' 589 | ``` 590 | 591 | ##### Метод getFirst 592 | 593 | Получает *первый* ресурс в списке. Это может быть `Closed`, `Opened`, `Removed`. 594 | 595 | ```php 596 | public mixed Collection::getFirst( void ) 597 | ``` 598 | 599 | **Примеры** 600 | 601 | ```php 602 | $collection->getFirst(); // object 'Resource/Closed' 603 | ``` 604 | 605 | ##### Метод getLast 606 | 607 | Метод коллекции, получает последний элемент. 608 | 609 | ```php 610 | public mixed Collection::getLast( void ) 611 | ``` 612 | 613 | **Примеры** 614 | 615 | ```php 616 | $collection->getLast(); // object 'Resource/Opened' 617 | ``` 618 | 619 | ##### Методы фильтрации 620 | 621 | Все это дело происходит на стороне API. Для коллекции доступны методы 622 | 623 | - setLimit 624 | - setMediaType 625 | - setOffset 626 | - setPreviewCrop 627 | - setPreview 628 | - setSort 629 | 630 | ### 1.3.8.1. Проверить ресурс на существование 631 | 632 | Проверить, существует ли ресурс на диске поможет ранее описанный метод `has` (вызывается без параметров). Если использовать с параметром - проверяет существует ли свойство. 633 | 634 | > Примечание: возможно в будущем метод будет упрощен. 635 | 636 | **Примеры** 637 | 638 | ```php 639 | $resource->has(); 640 | 641 | $resource->has('name'); // проверить, есть ли 'name' 642 | ``` 643 | 644 | ### 1.3.8.2. Получение информации о ресурсе 645 | 646 | Осуществляется с помощью основных методов получения информации, описанных ранее. 647 | 648 | **Примеры** 649 | 650 | ```php 651 | $resource->toObject(); 652 | 653 | $resource->get('items'); 654 | 655 | $resource->hasProperty('name'); 656 | 657 | $resource->has('type'); 658 | 659 | $resource->toArray(['name', 'type', 'size']); 660 | 661 | $resource->size; 662 | 663 | $resource['type']; 664 | 665 | $resource->get('custom_properties', []); 666 | ``` 667 | 668 | ### 1.3.8.3. Ресурс является файлом/папкой 669 | 670 | Для этого существуют методы **isFile** и **isDir**. 671 | 672 | ```php 673 | public boolean Объект::isFile( void ) 674 | 675 | public boolean Объект::isDir( void ) 676 | ``` 677 | 678 | **Примеры** 679 | 680 | ```php 681 | $resource->isFile(); // true 682 | 683 | $resource->isDir(); // false 684 | ``` 685 | 686 | ### 1.3.8.4. Ресурс публичный/или доступен только владельцу 687 | 688 | Проверить открыт ли доступ к файлу или папке позволяет метод `isPublish` 689 | 690 | ```php 691 | public boolean Объект::isPublish( void ) 692 | ``` 693 | 694 | **Примеры** 695 | 696 | ```php 697 | $resource->isPublish(); // false 698 | 699 | // отрыть доступ к ресурсу 700 | if ( ! $resource->isPublish()) 701 | { 702 | $resource->setPublish(true); 703 | } 704 | ``` 705 | 706 | ### 1.3.8.5. Путь к ресурсу на диске 707 | 708 | Для этого можно воспользоваться методом `getPath`. Этот путь использует SDK, но хоть значение может и отличаться от того, которое может возвращать Яндекс.Диск, такое не совпадение вполне корректно. 709 | 710 | ```php 711 | public string Объект::getPath( void ) 712 | ``` 713 | 714 | Примеры 715 | 716 | ```php 717 | $resource->getPath(); // disk:/файл.txt 718 | ``` 719 | 720 | ### 1.3.8.6. Добавление/удаление метаинформации для ресурса 721 | 722 | Добавленная метаинформация хранится в свойстве "custom_properties". Максимальная длина объекта (ключи + значения) 1024 байта. Значение не должно быть `NULL`. 723 | 724 | ```php 725 | public $this Closed::set(mixed $meta [, mixed $value = null]) 726 | ``` 727 | 728 | `$meta` - строка либо массив значений. 729 | 730 | 731 | `$value` - `NULL` чтобы удалить определённую метаинформацию когда `$meta` строка. 732 | 733 | **Примеры** 734 | 735 | ```php 736 | $resource->set('any', 'thing'); 737 | 738 | $resource->set([ 739 | 'any' => 'thing', 740 | 'thing' => 'any' 741 | ]); 742 | 743 | $resource['any'] = 'thing'; 744 | 745 | $resource->any = 'thing'; 746 | 747 | ``` 748 | 749 | #### Удаление информации 750 | 751 | Чтобы удалить метаинформацию необходимо установить значение `NULL`. 752 | 753 | **Примеры** 754 | 755 | ```php 756 | $resource->set('any', null); // удалить 'any' 757 | 758 | $resource->set('thing'); // удалить 'thing' 759 | 760 | unset($resource['any']); 761 | 762 | unset($resource->any); 763 | ``` 764 | 765 | #### метод getProperty 766 | 767 | Работает со свойством "custom_properties" - в нём хранится добавляемая метаинформация. Метод похож на метод `get`. 768 | 769 | > Примечание: возможно в будущем метод будет переименован. 770 | 771 | ```php 772 | public mixed Closed::getProperty(string $index [, mixed $default = null]) 773 | ``` 774 | 775 | `$index` - ключ, по которому получить значение. 776 | 777 | 778 | `$default` - значение по умолчанию, если такой индекс отсутствует - может принимать анонимную функцию, которая будет вызвана с текущим контекстом (только Resource\Closed). 779 | 780 | **Примеры** 781 | 782 | ```php 783 | $resource->getProperty('any'); 784 | 785 | $resource->get('thing12141', 'значение по умолчанию'); // вернет значение по умолчанию 786 | 787 | $resource->get('index', function (Resource\Closed $resource) { 788 | // анонимная функция будет вызвана с параметром 789 | // текущего контекста и значение по умолчанию 790 | // будет значение, возвращаемое этой функцией 791 | 792 | return 'значение по умолчанию'; 793 | }); 794 | 795 | ``` 796 | 797 | #### Метод getProperties 798 | 799 | Получает массив всей метаинформации. 800 | 801 | ```php 802 | public array Closed::getProperties( void ) 803 | ``` 804 | 805 | **Примеры** 806 | 807 | ```php 808 | $resource->getProperties(); // array 809 | 810 | // метод 'get' также может получать метаинформацию. 811 | // получить всю доступную метаинформацию в виде массива 812 | $resource->get('custom_properties', []); 813 | 814 | // получение информации без использования метода 'getProperty' 815 | $resource->get('custom_properties')['thing12141'] 816 | ``` 817 | 818 | ### 1.3.8.7. Удаление файла или папки 819 | 820 | Удалить совсем или поместить файл или папку в корзину можно методом `delete`. 821 | 822 | ```php 823 | public mixed delete([bool $permanently = false]) 824 | ``` 825 | 826 | `$permanently` - признак безвозвратного удаления. `FALSE` поместит ресурс в корзину (поведение по умолчанию). 827 | 828 | **Возвращаемые значения:** 829 | 830 | - `boolean` - результат выполнения. 831 | 832 | - `Arhitector\Yandex\Disk\Operation` - объект синхронной операции, если по мнению API Яндекс.Диска операция удаления длительная. 833 | 834 | - ~~`Arhitector\Yandex\Disk\Resource\Removed` - объект ресурса в корзине (не поддерживается).~~ 835 | 836 | **Примеры** 837 | 838 | ```php 839 | $resource->delete(); // в корзину 840 | 841 | $resource->delete(true); // удалить без помещения в корзину 842 | ``` 843 | 844 | ### 1.3.8.8. Перемещение файла или папки 845 | 846 | Перемещать файлы и папки на Диске можно, указывая новое положение ресурса. 847 | 848 | ```php 849 | public mixed Closed::move(mixed $destionation [, $overwrite = FALSE] ) 850 | ``` 851 | 852 | `$destination` - новое расположение ресурса. Может быть `строкой` или `Resource\Closed`. 853 | 854 | 855 | `$overwrite` - `boolean` признак перезаписи, если по новому пути существует ресурс. `TRUE` перезапишет (поведение по умолчанию `FALSE` - не перезаписывать). 856 | 857 | **Возвращаемые значения** 858 | 859 | `bool` или объект `Arhitector\Yandex\Disk\Operation` 860 | 861 | **Примеры** 862 | 863 | ```php 864 | $resource->move('/путь/до/файла.txt'); 865 | 866 | $resource->move('app:/новая папка', true); 867 | 868 | $resource->move($resource2); 869 | ``` 870 | 871 | ### 1.3.8.9. Создание папки 872 | 873 | Если ресурс уже существует будет вызвано исключение `AlreadyExists`. 874 | 875 | ```php 876 | public $this Closed::create( void ) 877 | ``` 878 | 879 | **Примеры** 880 | 881 | ```php 882 | $resource->create(); 883 | ``` 884 | 885 | ### 1.3.8.10. Публикация ресурса\Закрытие доступа 886 | 887 | Открывает доступ к ресурсу из вне по публичной ссылке. Опубликованные ресурсы управляются своим объектом `Arhitector\Yandex\Disk\Resource\Opened`. 888 | 889 | ```php 890 | public mixed Closed::setPublish([bool $publish = true]) 891 | ``` 892 | 893 | `$publish` - признак публичности, `TRUE` сделать ресурс публичным (поведение по умолчанию), `FALSE` отменить публикации ресурса. 894 | 895 | **Возвращаемые значения** 896 | 897 | `Arhitector\Yandex\Disk\Resource\Closed` возвращается когда доступ закрыт. 898 | 899 | 900 | `Arhitector\Yandex\Disk\Resource\Opened` возвращается если был открыт доступ к ресурсу. 901 | 902 | У ресурса с открытым доступом существует дополнительная информация, такая как `public_key` или `public_url`. Также `docviewer` возвращает ссылку доступную всем из вне. 903 | 904 | **Примеры** 905 | 906 | ```php 907 | $resource->setPublish(); // открывает доступ 908 | 909 | $resource-setPublish(true); // открывает доступ 910 | 911 | $resource->setPublish(false); // закрывает доступ 912 | 913 | $resource->isPublish(); // true если ресурс с открытым доступом 914 | 915 | $resource->public_url; // URL адрес 916 | ``` 917 | ### 1.3.8.11. Скачивание файла 918 | 919 | Метод `download` безопасен от переполнения памяти и может быть использован для скачивания файлов и папок (автоматически в виде zip-архива). 920 | 921 | ```php 922 | public bool Closed::download(mixed $destination [, bool $overwrite = false]) 923 | ``` 924 | 925 | `$destination` - позволяет указать куда будет сохранён ресурс. 926 | 927 | Поддерживаются следующие типы: 928 | 929 | - `string` - путь, по которому будет записан ресурс. 930 | - `resource` - дескриптор файла, открытый на запись. 931 | - `StreamInterface` - объект на запись, реализующий PSR StreamInterface. 932 | 933 | `$overwrite` - используется совместно с `$destination` строкового типа `string`, определяет поведение (перезаписать/не перезаписывать), если по такому пути существует локальный файл. 934 | 935 | **Возвращаемые значения** 936 | 937 | `TRUE` или `FALSE`, а также вызывает исключения по типовым событиям, например, `AlreadyExistsException` или `NotFoundException`. 938 | 939 | **Примеры** 940 | 941 | Скачать файл в локальную папку. 942 | 943 | ```php 944 | // без перезаписи 945 | $resource->download(__DIR__.'/файл.txt'); 946 | 947 | // без перезаписи 948 | $resource->download(__DIR__.'/файл.txt', false); 949 | 950 | // с перезапсью 951 | $resource->download(__DIR__.'/файл.txt', true); 952 | ``` 953 | 954 | Запись в открытый дескриптор. 955 | 956 | ```php 957 | // открыть любой дескриптор 958 | $fp = fopen(__DIR__.'/файл.txt', 'wb+'); 959 | 960 | // или и т.д. 961 | $fp = fopen('php://memory', 'r+b'); 962 | 963 | $resource->download($fp); 964 | 965 | // продолжить работу ... 966 | fseek($fp, 0); 967 | ``` 968 | 969 | Использовать обертку над потоком так же просто. 970 | 971 | ```php 972 | $stream = new Stream('php://temp', 'r+'); 973 | 974 | $resource->download($stream); 975 | 976 | var_dump($stream->getSize()); 977 | ``` 978 | 979 | ### 1.3.8.12. Копирование файла или папки 980 | 981 | Сделать копию ресурса. 982 | 983 | ```php 984 | public bool Closed::copy(mixed $destination [,bool $overwrite = false]) 985 | ``` 986 | 987 | `$destination` - путь до нового ресурса. 988 | 989 | Может принимать значения: 990 | 991 | - `string` - строка, путь от корня папки приложения или корня Яндекс.Диска. 992 | - `Arhitector\Yandex\Disk\Resource\Closed` - инициализированный объект другого ресурса. 993 | 994 | `$overwrite` - признак перезаписи, если по указанному пути существует ресурс. Поведение по умолчанию `FALSE`. 995 | 996 | **Возвращаемые значения** 997 | 998 | `TRUE` или `FALSE`, а также `Arhitector\Yandex\Disk\Operation` в случае длительного копирования. 999 | 1000 | **Примеры** 1001 | 1002 | ```php 1003 | // сделать копию файла 1004 | $resource->copy('папка/файл-копия.txt'); 1005 | 1006 | // сделать копию папки 1007 | $resource->copy('app:/папка-копия'); 1008 | 1009 | // сделать копию $resource по пути 'копия/путь до файла.txt' 1010 | $resource2 = $disk->getResource('копия/путь до файла.txt'); 1011 | $resource->copy($resource2, true); 1012 | ``` 1013 | 1014 | ### 1.3.8.13. Загрузка файла 1015 | 1016 | Метод `upload` безопасен от утечки памяти и используется для загрузки файлов на Яндекс.Диск. Может загружать как файлы расположенные локально, в локальной папке, так и, файлы расположенные на удаленном хостинге/сервере и доступные по URL-адресу. 1017 | 1018 | ```php 1019 | public mixed upload(mixed $file_path [, bool $overwrite = false [, bool $disable_redirects = false]]) 1020 | ``` 1021 | 1022 | `$file_path` - может быть как путь к локальному файлу, так и URL к файлу. 1023 | 1024 | Принимает значения: 1025 | 1026 | - `string` - путь до локального файла или URL-адрес. 1027 | - `resource` - дескриптор файла, открытый на чтение. 1028 | 1029 | `$overwrite` - признак перезаписи, если ресурс на Яндекс.Диске существует. Параметр не влияет на загрузку файлов по URL-адресу. 1030 | 1031 | `$disable_redirects` - параметр влияет на файлы, загружаемые по URL-адресу. `TRUE` помогает запретить перенаправление по адресу. Поведение по умолчанию `FALSE` - пре адресация разрешена. 1032 | 1033 | **Примеры** 1034 | 1035 | Загрузка локального файла. 1036 | 1037 | ```php 1038 | $resource->upload(__DIR__.'/файл.txt'); 1039 | 1040 | // загрузка с перезаписью 1041 | $resource->upload(__DIR__.'/файл.txt', true); 1042 | 1043 | // если передан дескриптор файла, загрузка с перезаписью 1044 | $fp = fopen(__DIR__.'/файл.txt', 'rb'); 1045 | $resource->upload($fp, true); 1046 | ``` 1047 | 1048 | Загрузка файлов, расположенных на удалённом сервере. Возвращает объект операции `Arhitector\Yandex\Disk\Operation`. 1049 | 1050 | ```php 1051 | $operation = $resource->upload('http://домен.ру/файл.zip'); 1052 | 1053 | // запретить пере адресацию. 1054 | $operation = $resource->upload('https://домен.ру/файл.zip', null, true); 1055 | ``` 1056 | 1057 | ### 1.3.8.14. Методы фильтрации 1058 | 1059 | Объект `Arhitector\Yandex\Disk\Resource\Closed` поддерживает: 1060 | 1061 | - setLimit 1062 | - setOffset 1063 | - setPreviewCrop 1064 | - setPreview 1065 | - setSort 1066 | 1067 | ## 1.3.9. Работа с публичными ресурсами. 1068 | 1069 | 1070 | Работа с ресурсами с открытым доступом осуществляется через методы `Disk::getPublishResource` и `Disk::getPublishResources`, доступ к которым имеет владелец диска. 1071 | 1072 | #### Метод Disk::getPublishResource 1073 | 1074 | Получает объектно ориентированное представление конкретного ресурса на Яндекс.Диске с открытым доступом. 1075 | 1076 | ```php 1077 | public Resource\Closed Disk::getPublishResource(string $public_key [, int $limit = 20 [, int $offset = 0]]) 1078 | ``` 1079 | 1080 | `$public_key` - публичный ключ или URL-адрес ресурса с открытым доступом. 1081 | 1082 | `$limit` - Количество ресурсов в ответе, если это папка. 1083 | 1084 | `$offset` - Смещение. Задаётся для списка всех файлов или если ресурс является папка, то задаёт смещение вложенных в папку ресурсов. 1085 | 1086 | **Примеры** 1087 | 1088 | Получить объект ресурса. 1089 | 1090 | ```php 1091 | /** 1092 | * @var Arhitector\Yandex\Disk\Resource\Opened $publicResource 1093 | */ 1094 | $publicResource = $disk->getResource('https://yadi.sk/d/g0N4hNtXcrq22'); 1095 | 1096 | $publicResource = $disk->getResource('wICbu9SPnY3uT4tFA6P99YXJwuAr2TU7oGYu1fTq68Y=', 10, 0); 1097 | ``` 1098 | 1099 | Установить `$limit` и `offset` можно и после получения объекта ресурса методами `setLimit` и `setOffset`. 1100 | 1101 | ```php 1102 | 1103 | $publicResource->setLimit(100); 1104 | 1105 | $publicResource->setOffset(200); 1106 | ``` 1107 | 1108 | #### Метод Disk::getPublishResources, список всех опубликованных файлов. 1109 | 1110 | Получает список всех файлов на Яндекс.Диске с открытым доступом и т.д. Список представлен объектом `Arhitector\Yandex\Disk\Resource\Collection`. 1111 | 1112 | ```php 1113 | public Resource\Collection Disk::getPublishResources([, int $limit = 20 [, int $offset = 0]]) 1114 | ``` 1115 | 1116 | Здесь доступны методы фильтрации, основные методы получения информации и ряд других. 1117 | 1118 | **Примеры** 1119 | 1120 | ```php 1121 | /** 1122 | * Получить список всех файлов 1123 | * 1124 | * @var Disk\Resource\Collection $collection 1125 | */ 1126 | $collection = $disk->getPublishResources(); 1127 | 1128 | $disk->getPublishResources(100, 15); 1129 | ``` 1130 | 1131 | ##### Методы фильтрации 1132 | 1133 | Все это дело происходит на стороне API. Для коллекции доступны методы 1134 | 1135 | - setLimit 1136 | - setMediaType 1137 | - setOffset 1138 | - setPreviewCrop 1139 | - setPreview 1140 | - setSort 1141 | 1142 | ### 1.3.9.1. Получить публичный ключ 1143 | 1144 | Получает публичный ключ или URL, который был использован для получения доступа к ресурсу. 1145 | 1146 | ```php 1147 | public string Opened::getPublicKey( void ) 1148 | ``` 1149 | 1150 | **Примеры** 1151 | 1152 | ```php 1153 | $publicResource->getPublicKey(); 1154 | ``` 1155 | 1156 | ### 1.3.9.2. Получает прямую ссылку 1157 | 1158 | Получить прямую ссылку на скачивание файла или папки. 1159 | 1160 | > Примечание: возвращаемая ссылка действует ("живет") пару часов. 1161 | 1162 | > Примечание: метод не поддерживает получение ссылок на ресурсы внутри публичной папки. Эта возможность реализуема, но не реализована. 1163 | 1164 | ```php 1165 | public string Opened::getLink( void ) 1166 | ``` 1167 | 1168 | **Примеры** 1169 | 1170 | ```php 1171 | $publicResource->getLink(); 1172 | ``` 1173 | 1174 | ### 1.3.9.3. Скачивание публичного файла или папки. 1175 | 1176 | Скачивание публичного файла или папки (в виде zip-архива). 1177 | 1178 | ```php 1179 | public bool Opened::download(mixed $destination [, bool $overwrite = false [, bool $check_hash = false]]) 1180 | ``` 1181 | 1182 | `$destination` - Путь, по которому будет сохранён файл 1183 | 1184 | Принимает значения: 1185 | 1186 | - `string` - файловый путь. 1187 | - `resource` - открытый на запись дескриптор файла. 1188 | - `StreamInterface` - поток, открытый на запись. 1189 | 1190 | `$overwrite` - флаг перезаписи, если `$destination` является файловым путем. `FALSE` - поведение по умолчанию. 1191 | 1192 | `$check_hash` - провести проверку целостности скачанного файла. Значение `TRUE` позволяет проверить `md5` хеш скачанного файла. По умолчанию `FALSE`. 1193 | 1194 | **Примеры** 1195 | 1196 | ```php 1197 | $publicResource->download(__DIR__.'/file.txt'); 1198 | 1199 | $publicResource->download(__DIR__.'/file.txt', true); 1200 | 1201 | $publicResource->download(__DIR__.'/file.txt', true, true); 1202 | ``` 1203 | 1204 | Запись в открытый дескриптор. 1205 | 1206 | ```php 1207 | // открыть любой дескриптор 1208 | $fp = fopen(__DIR__.'/файл.txt', 'wb+'); 1209 | 1210 | // или и т.д. 1211 | $fp = fopen('php://memory', 'r+b'); 1212 | 1213 | // true - провести проверку целостности скачанного файла 1214 | $publicResource->download($fp, false, true); 1215 | 1216 | // продолжить работу ... 1217 | fseek($fp, 0); 1218 | ``` 1219 | 1220 | Использовать обертку над потоком так же просто. 1221 | 1222 | ```php 1223 | $stream = new Stream('php://temp', 'r+'); 1224 | 1225 | $publicResource->download($stream); 1226 | 1227 | var_dump($stream->getSize()); 1228 | ``` 1229 | 1230 | ### 1.3.9.4. Есть ли доступ к этому файлу от имени владельца. 1231 | 1232 | /** 1233 | * Этот файл или такой же находится на моём диске 1234 | * Метод требует Access Token 1235 | * 1236 | * @return boolean 1237 | */ 1238 | 1239 | public function hasEqual() 1240 | 1241 | ### 1.3.9.5. Сохранение публичного файла в «Загрузки». 1242 | 1243 | /** 1244 | * Сохранение публичного файла в «Загрузки» или отдельный файл из публичной папки 1245 | * 1246 | * @param string $name Имя, под которым файл следует сохранить в папку «Загрузки» 1247 | * @param string $path Путь внутри публичной папки. 1248 | * 1249 | * @return mixed 1250 | */ 1251 | public function save($name = null, $path = null) 1252 | 1253 | ### 1.3.9.6. Установить путь внутри публичной папки. 1254 | 1255 | /** 1256 | * Устанавливает путь внутри публичной папки 1257 | * 1258 | * @param string $path 1259 | * 1260 | * @return $this 1261 | */ 1262 | public function setPath($path) 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | ## 1.3.10. Работа с файлами в корзине. 1269 | 1270 | /** 1271 | * Ресурсы в корзине. 1272 | * 1273 | * @param string $path путь к файлу в корзине 1274 | * @param int $limit 1275 | * @param int $offset 1276 | * 1277 | * @return \Arhitector\Yandex\Disk\Resource\Removed 1278 | * @example 1279 | * 1280 | * $disk->getTrashResource('file.ext') -> toArray() // файл в корзине 1281 | * $disk->getTrashResource('trash:/file.ext') -> delete() 1282 | */ 1283 | public function getTrashResource($path, $limit = 20, $offset = 0) 1284 | 1285 | /** 1286 | * Содержимое всей корзины. 1287 | * 1288 | * @param int $limit 1289 | * @param int $offset 1290 | * 1291 | * @return \Arhitector\Yandex\Disk\Resource\Collection 1292 | */ 1293 | public function getTrashResources($limit = 20, $offset = 0) 1294 | 1295 | ### 1.3.10.1. Восстановить ресурс из корзины. 1296 | 1297 | /** 1298 | * Восстановление файла или папки из Корзины 1299 | * В корзине файлы с одинаковыми именами в действительности именют постфикс к имени в виде unixtime 1300 | * 1301 | * @param mixed $name оставляет имя как есть и если boolean это заменяет overwrite 1302 | * @param boolean $overwrite 1303 | * @return mixed 1304 | */ 1305 | public function restore($name = null, $overwrite = false) 1306 | 1307 | ### 1.3.10.2. Удалить ресурс из корзины. 1308 | 1309 | /** 1310 | * Удаление файла или папки 1311 | * 1312 | * @return mixed 1313 | */ 1314 | public function delete() 1315 | 1316 | 1317 | ## 1.3.11. Очистка корзины. 1318 | 1319 | /** 1320 | * Очистить корзину. 1321 | * 1322 | * @return bool|\Arhitector\Yandex\Disk\Operation 1323 | */ 1324 | public function cleanTrash() 1325 | 1326 | ## 1.3.12. Последние загруженные файлы. 1327 | /** 1328 | * Последние загруженные файлы 1329 | * 1330 | * @param integer $limit 1331 | * @param integer $offset 1332 | * 1333 | * @return \Arhitector\Yandex\Disk\Resource\Collection 1334 | * 1335 | * @example 1336 | * 1337 | * $disk->uploaded(limit, offset) // коллекия закрытых ресурсов 1338 | */ 1339 | public function uploaded($limit = 20, $offset = 0) 1340 | 1341 | ## 1.3.13. Синхронные операции. 1342 | 1343 | /** 1344 | * Получить статус операции. 1345 | * 1346 | * @param string $identifier идентификатор операции или NULL 1347 | * 1348 | * @return \Arhitector\Yandex\Disk\Operation 1349 | * 1350 | * @example 1351 | * 1352 | * $disk->getOperation('identifier operation') 1353 | */ 1354 | public function getOperation($identifier) 1355 | 1356 | /** 1357 | * Возвращает количество асинхронных операций экземпляра. 1358 | * 1359 | * @return int 1360 | */ 1361 | public function count() 1362 | 1363 | /** 1364 | * Получить все операции, полученные во время выполнения сценария 1365 | * 1366 | * @return array 1367 | * 1368 | * @example 1369 | * 1370 | * $disk->getOperations() 1371 | * 1372 | * array (size=124) 1373 | * 0 => 'identifier_1', 1374 | * 1 => 'identifier_2', 1375 | * 2 => 'identifier_3', 1376 | */ 1377 | public function getOperations() 1378 | 1379 | ## 1.3.14. Методы фильтрации. 1380 | 1381 | /** 1382 | * Количество ресурсов, вложенных в папку, описание которых следует вернуть в ответе 1383 | * 1384 | * @param integer $limit 1385 | * @param integer $offset установить смещение 1386 | * 1387 | * @return $this 1388 | */ 1389 | public function setLimit($limit, $offset = null) 1390 | 1391 | 1392 | /** 1393 | * Количество вложенных ресурсов с начала списка, которые следует опустить в ответе 1394 | * 1395 | * @param integer $offset 1396 | * 1397 | * @return $this 1398 | */ 1399 | public function setOffset($offset) 1400 | 1401 | /** 1402 | * Атрибут, по которому сортируется список ресурсов, вложенных в папку. 1403 | * 1404 | * @param string $sort 1405 | * @param boolean $inverse TRUE чтобы сортировать в обратном порядке 1406 | * 1407 | * @return $this 1408 | * @throws \UnexpectedValueException 1409 | */ 1410 | public function setSort($sort, $inverse = false) 1411 | 1412 | 'Допустимые значения сортировки - name, path, created, modified, size' 1413 | 1414 | /** 1415 | * Тип файлов, которые нужно включить в список 1416 | * 1417 | * @param string $media_type 1418 | * 1419 | * @return $this 1420 | * @throws \UnexpectedValueException 1421 | */ 1422 | public function setMediaType($media_type) 1423 | Тип файлов, которые нужно включить в список. Диск определяет тип каждого файла при загрузке. 1424 | Чтобы запросить несколько типов файлов, можно перечислить их в значении параметра через запятую. Например, media_type="audio,video". 1425 | Поддерживаемые типы: 1426 | audio — аудио-файлы. 1427 | backup — файлы резервных и временных копий. 1428 | book — электронные книги. 1429 | compressed — сжатые и архивированные файлы. 1430 | data — файлы с базами данных. 1431 | development — файлы с кодом (C++, Java, XML и т. п.), а также служебные файлы IDE. 1432 | diskimage — образы носителей информации в различных форматах и сопутствующие файлы (например, CUE). 1433 | document — документы офисных форматов (Word, OpenOffice и т. п.). 1434 | encoded — зашифрованные файлы. 1435 | executable — исполняемые файлы. 1436 | flash — файлы с флэш-видео или анимацией. 1437 | font — файлы шрифтов. 1438 | image — изображения. 1439 | settings — файлы настроек для различных программ. 1440 | spreadsheet — файлы офисных таблиц (Numbers, Lotus). 1441 | text — текстовые файлы. 1442 | unknown — неизвестный тип. 1443 | video — видео-файлы. 1444 | web — различные файлы, используемые браузерами и сайтами (CSS, сертификаты, файлы закладок). 1445 | 1446 | /** 1447 | * Получает установленное значение. 1448 | * 1449 | * @return string 1450 | */ 1451 | public function getMediaType() 1452 | 1453 | /** 1454 | * Все возможные типы файлов 1455 | * 1456 | * @return array 1457 | */ 1458 | public function getMediaTypes() 1459 | 1460 | /** 1461 | * Обрезать превью согласно размеру 1462 | * 1463 | * @param boolean $crop 1464 | * 1465 | * @return $this 1466 | */ 1467 | public function setPreviewCrop($crop) 1468 | Параметр позволяет обрезать превью согласно размеру, заданному в значении параметра preview_size. 1469 | Допустимые значения: 1470 | «false» — параметр игнорируется. Это значение используется по умолчанию. 1471 | «true» — превью обрезается следующим образом: 1472 | Если передана только ширина или высота, картинка уменьшается до этого размера с сохранением пропорций. Затем из центра уменьшенного изображения также вырезается квадрат с заданной стороной. 1473 | Если передан точный размер (например, «120x240»), из центра оригинального изображения вырезается фрагмент максимального размера в заданных пропорциях ширины и высоты. Затем вырезанный фрагмент масштабируется до указанных размеров. 1474 | 1475 | /** 1476 | * Размер уменьшенного превью файла 1477 | * 1478 | * @param mixed $preview S, M, L, XL, XXL, XXXL, <ширина>, <ширина>x, x<высота>, <ширина>x<высота> 1479 | * 1480 | * @return $this 1481 | * @throws \UnexpectedValueException 1482 | */ 1483 | public function setPreview($preview) 1484 | Вы можете задать как точный размер превью, так и размер одной из сторон. Получившееся изображение можно обрезать до квадрата с помощью параметра preview_crop. 1485 | Варианты значений: 1486 | Предопределенный размер большей стороны. 1487 | Картинка уменьшается до указанного размера по большей стороне, пропорции исходного изображения сохраняются. Например, для размера «S» и картинки размером 120×200 будет сгененерировано превью размером 90×150, а для картинки 300×100 — превью размером 150×50. 1488 | Поддерживаемые значения: 1489 | «S» — 150 пикселей; 1490 | «M» — 300 пикселей; 1491 | «L» — 500 пикселей; 1492 | «XL» — 800 пикселей; 1493 | «XXL» — 1024 пикселей; 1494 | «XXXL» — 1280 пикселей. 1495 | Точная ширина (например, «120» или «120x») или точная высота (например, «x145»). 1496 | Картинка уменьшается до указанной ширины или высоты, пропорции исходного изображения сохраняются. 1497 | Если передан параметр preview_crop, из центра уменьшенного изображения также вырезается квадрат с заданной стороной. 1498 | Точный размер (в формате <ширина>x<высота>, например «120x240»). 1499 | Картинка уменьшается до меньшего из указанных размеров, пропорции исходного изображения сохраняются. 1500 | Если передан параметр preview_crop, из центра оригинального изображения вырезается фрагмент максимального размера в заданных пропорциях ширины и высоты (в примере — 1/2). Затем вырезанный фрагмент масштабируется до указанных размеров. 1501 | 1502 | 1503 | /** 1504 | * Получает установленное значение "setPreview". 1505 | * 1506 | * @return string 1507 | */ 1508 | public function getPreview() 1509 | 1510 | /** 1511 | * Получает установленное значение "setPreviewCrop". 1512 | * 1513 | * @return string 1514 | */ 1515 | public function getPreviewCrop() 1516 | 1517 | /** 1518 | * Относительный путь к ресурсу внутри публичной папки. 1519 | * 1520 | * @param string $path 1521 | * 1522 | * @return $this 1523 | */ 1524 | public function setRelativePath($path) 1525 | 1526 | /** 1527 | * Получает установленное значение. 1528 | * 1529 | * @return string 1530 | */ 1531 | public function getRelativePath() 1532 | 1533 | /** 1534 | * Тип ресурса 1535 | * 1536 | * @param string $type 1537 | * 1538 | * @return $this 1539 | * @throws \UnexpectedValueException 1540 | */ 1541 | public function setType($type) 1542 | 1543 | 1544 | /** 1545 | * Получает установленное значение. 1546 | * 1547 | * @return string 1548 | */ 1549 | public function getType() 1550 | 1551 | 1552 | ## 1.3.15 События. 1553 | 1554 | Ресурсы поддерживают свои события. Чтобы получить больше информации читайте описание событий клиента (описано ниже). Список доступных событий: 1555 | 1556 | - **disk.downloaded** - событие наступает всякий раз когда именно этот ресурс был скачан. 1557 | - **disk.uploaded** - событие наступает когда ресурс загружен. 1558 | - **disk.operation** - наступает когда Яндекс.Диск выполняет асинхронную операцию с ресурсом (например, перемещение большой папки). 1559 | - **disk.delete** - наступает тогда, когда именно этот ресурс удаляется, может отменить удаление. 1560 | 1561 | Отличие от событий делигируемых клиентом заключается в видимости этих событий. Клиент устанавливает события для всех ресурсов, не смотря на это каждый ресурс может принимать свои собственные события. 1562 | 1563 | Событие может принимать несколько обработчиков. Обработчики событий не заменяют друг друга, а добавляются в очередь. 1564 | 1565 | **Примеры** 1566 | 1567 | ```php 1568 | $resource->addListeners(); 1569 | 1570 | ``` 1571 | 1572 | ## 1.3.16 Событие `uploaded` 1573 | 1574 | Вызывается после выполнения запроса на загрузку локального файла. 1575 | 1576 | ```php 1577 | use Arhitector\Yandex\Disk; 1578 | use Arhitector\Yandex\Disk\Resource\Closed; 1579 | use League\Event\Event; 1580 | use Psr\Http\Message\ResponseInterface; 1581 | use Psr\Http\Message\StreamInterface; 1582 | 1583 | // ... 1584 | $disk->addListener('uploaded', function (Event $event, Closed $resource, Disk $disk, StreamInterface $uploadedStream, ResponseInterface $response) { 1585 | // $event - событие 1586 | // $resource - тоже самое что и $resource 1587 | // $disk - клиент 1588 | // $uploadedStream - в данном примере файл file_path.pdf обернутый в Stream 1589 | // $response - Ответ от Я.Диска. $response->getBody() - не содержит ничего (см. документацию API Я.Диска) 1590 | }); 1591 | 1592 | // ... 1593 | $resource->upload(__DIR__.'/file_path.pdf'); 1594 | 1595 | ``` 1596 | 1597 | 1598 | 1599 | 1600 | 1601 | 1602 | 1603 | ## Стандарты кодирования 1604 | 1605 | Эти стандарты должны соблюдаться при внесении изменений в код. В основе стандарта используется PSR-2 с некоторыми изменениями. 1606 | 1607 | ### Управляющие структуры 1608 | 1609 | Ключевые слова, такие как **if**, **for**, **foreach**, **while**, **switch** должны сопровождаться пробелом перед списком параметров/аргументов. Фигурные скобки должны располагаться на новой линии и разрыв должен соблюдать ту же вложенность. 1610 | 1611 | ```php 1612 | if ($arg === true) 1613 | { 1614 | // некоторый код 1615 | } 1616 | elseif ($arg === null) 1617 | { 1618 | // некоторый код 1619 | } 1620 | else 1621 | { 1622 | // некоторый код 1623 | } 1624 | 1625 | foreach ($array as $key => $value) 1626 | { 1627 | // некоторый код 1628 | } 1629 | 1630 | for ($i = 0; $i < $max; $i++) 1631 | { 1632 | // некоторый код 1633 | } 1634 | 1635 | while ($i < $max) 1636 | { 1637 | // некоторый код 1638 | } 1639 | 1640 | do 1641 | { 1642 | // некоторый код 1643 | } 1644 | while ($i < $max) 1645 | 1646 | switch ($var) 1647 | { 1648 | case 'value1': 1649 | // некоторый код 1650 | break; 1651 | 1652 | default : 1653 | // некоторый код 1654 | break; 1655 | } 1656 | ``` 1657 | 1658 | ### Сравнения и логические операторы 1659 | 1660 | Оператор **!** должен быть окружён пробелами с обеих сторон. 1661 | 1662 | ```php 1663 | if ($var == false and $other_var != 'some_value') 1664 | if ($var === false or my_function() !== false) 1665 | if ( ! $var) 1666 | ``` 1667 | 1668 | 1669 | 1670 | 1671 | 1672 | 1673 | 1674 | ## 1. Базовый клиент для общения с сервисами Яндекса 1675 | 1676 | Базовый клиент предоставляет функциональность для прохождения аунтификации на сервисах Яндекса, использующих протокол OAuth. Наследуется другими клиентами для определённых сервисов (например, *Mackey\Yandex\Disk* также наследует методы базового клиента). 1677 | 1678 | ```php 1679 | public void Client::__construct([string $token = null]) 1680 | ``` 1681 | 1682 | **$token** - необязательный параметр, OAuth-токен приложения. 1683 | 1684 | **Примеры** 1685 | 1686 | ```php 1687 | $client = new Client(); 1688 | 1689 | $client = new Client('0c4181a7c2cf4521964a72ff57a34a07'); 1690 | ``` 1691 | 1692 | ### 1.1. Формат обмена данными 1693 | 1694 | Получает информацию о формате, в котором происходит общение с сервисами. 1695 | 1696 | ```php 1697 | public string Client::getContentType( void ) 1698 | ``` 1699 | 1700 | **Примеры** 1701 | 1702 | ```php 1703 | $client->getContentType(); // application/json 1704 | ``` 1705 | 1706 | ### 1.2. Установить ID приложения 1707 | 1708 | Устанавливает ID зарегистрированного приложения. 1709 | 1710 | ```php 1711 | public this Client::setClientOauth(string $client_id) 1712 | ``` 1713 | 1714 | **$client_id** - строка, ID приложения 1715 | 1716 | **Примеры** 1717 | 1718 | ``` 1719 | $client->setClientOauth('123456789'); 1720 | ``` 1721 | 1722 | ### 1.3. Получить ID приложения 1723 | 1724 | Возвращает ID приложения, если таковой был ранее установлен с помощью метода *setClientOauth*. 1725 | 1726 | ```php 1727 | public mixed Client::getClientOauth( void ) 1728 | ``` 1729 | 1730 | **Пример** 1731 | 1732 | ```php 1733 | $client->getClientOauth(); // null 1734 | 1735 | $client->getClientOauth(); // string '123456789' 1736 | ``` 1737 | 1738 | ### 1.4. Установить пароль приложения 1739 | 1740 | Устанавливает пароль приложения. Пароль приложения выдается при регистрации этого приложения. 1741 | 1742 | ```php 1743 | public this Client::setClientOauthSecret(string $client_secret) 1744 | ``` 1745 | 1746 | **Примеры** 1747 | 1748 | ```php 1749 | $client->setClientOauthSecret('--------'); 1750 | ``` 1751 | 1752 | ### 1.5. Получить пароль приложения 1753 | 1754 | Возвращает ранее установленный пароль приложения или NULL. 1755 | 1756 | ```php 1757 | public mixed Client::getClientOauthSecret( void ) 1758 | ``` 1759 | 1760 | **Примеры** 1761 | 1762 | ```php 1763 | $client->getClientOauthSecret(); // null 1764 | 1765 | $client->getClientOauthSecret(); // string '--------' 1766 | ``` 1767 | 1768 | ### 1.6. Установить OAuth-токен 1769 | 1770 | Устанавливает OAuth-токен для прохождения аунтификация на сервисах. Не ве сервисы требуют OAuth-токен. 1771 | 1772 | ```php 1773 | public this Client::setAccessToken(string $token) 1774 | ``` 1775 | 1776 | **Примеры** 1777 | 1778 | ```php 1779 | $client->setAccessToken('0c4181a7c2cf4521964a72ff57a34a07'); 1780 | ``` 1781 | 1782 | ### 1.7. Получить установленный OAuth-токен 1783 | 1784 | Получает ранее установленный OAuth-токен или NULL. 1785 | 1786 | ```php 1787 | public mixed Client::getAccessToken( void ); 1788 | ``` 1789 | 1790 | **Примеры** 1791 | 1792 | ```php 1793 | $client->getAccessToken(); // null 1794 | 1795 | $client->getAccessToken(); // string '0c4181a7c2cf4521964a72ff57a34a07' 1796 | ``` 1797 | 1798 | ### 1.8. Запросить новый или обновить уже выданный OAuth-токен 1799 | 1800 | Позволяет получить OAuth-токен или обновить уже выданный ранее токен для приложения. 1801 | 1802 | ```php 1803 | public mixed refreshAccessToken(string $username, string $password [, bool $onlyToken = false]) 1804 | ``` 1805 | 1806 | В случае успеха возвращает объект с информацией или только OAuth-токен если передан параметр $onlyToken = true. 1807 | 1808 | **$username** - имя пользователя аккаунта, на котором зарегистрировано приложение 1809 | 1810 | **$password** - пароль от аккаунта 1811 | 1812 | **$onlyToken** - вернуть только строковый токен 1813 | 1814 | **Примеры** 1815 | 1816 | ```php 1817 | $client->refreshAccessToken('username', 'password'); 1818 | 1819 | /* object(stdClass)[28] 1820 | public 'token_type' => string 'bearer' (length=6) 1821 | public 'access_token' => string 'c7621`6149032dwf9a6a7ca765eb39b8' (length=32) 1822 | public 'expires_in' => int 31536000 1823 | public 'uid' => int 241`68329 1824 | public 'created_at' => int 1456882032 */ 1825 | 1826 | $client->refreshAccessToken('username', 'password', true); 1827 | 1828 | /* string 'c7621`6b09032dwf9a6a7ca765eb39b8' (length=32) */ 1829 | ``` 1830 | 1831 | ### 1.9. Установить обработчик события 1832 | 1833 | SDK поддерживает события. Каждый сервис имеет свой набор возможных событий. Перейдите на для получения более полной информации. 1834 | 1835 | ```php 1836 | public this Client::addListener(string $event, mixed $listener [, int $priority = ListenerAcceptorInterface::P_NORMAL]) 1837 | ``` 1838 | 1839 | **$event** - событие 1840 | 1841 | **$listener** - обработчик ListenerInterface или callable 1842 | 1843 | **$priority** - приоритет. **League\Event\EmitterInterface** предопределяет 3 приоритета: 1844 | - EmitterInterface::P_HIGH: 100 1845 | - EmitterInterface::P_NORMAL: 0 1846 | - EmitterInterface::P_LOW: -100 1847 | 1848 | 1849 | **Примеры** 1850 | 1851 | ```php 1852 | $client->addListener('disk.downloaded', function (Event $event, $resource) { 1853 | // скачивание файла завершено 1854 | }); 1855 | ``` 1856 | 1857 | ### 1.10. Удалить обработчик для события 1858 | 1859 | Удаляет ранее установленный обработчик. 1860 | 1861 | ```php 1862 | public this Client::removeListener(string $event, mixed $listener) 1863 | ``` 1864 | 1865 | **$event** - событие 1866 | 1867 | **$listener** - обработчик ListenerInterface или callable 1868 | 1869 | **Примеры** 1870 | 1871 | ```php 1872 | $client->removeListener('disk.downloaded', function (Event $event, $resource) { 1873 | 1874 | }); 1875 | ``` 1876 | 1877 | ### 1.11. Установить одноразовый обработчик 1878 | 1879 | Устанавливает обработчик. 1880 | 1881 | ```php 1882 | public this addOneTimeListener(string $event, mixed $listener [, int $priority = ListenerAcceptorInterface::P_NORMAL]) 1883 | ``` 1884 | 1885 | **$event** - событие 1886 | 1887 | **$listener** - обработчик ListenerInterface или callable 1888 | 1889 | **$priority** - приоритет. **League\Event\EmitterInterface** предопределяет 3 приоритета: 1890 | - EmitterInterface::P_HIGH: 100 1891 | - EmitterInterface::P_NORMAL: 0 1892 | - EmitterInterface::P_LOW: -100 1893 | 1894 | **Примеры** 1895 | 1896 | ```php 1897 | $client->addOneTimeListener('disk.downloaded', function (Event $event, $resource) { 1898 | // скачивание файла завершено 1899 | }); 1900 | ``` 1901 | 1902 | ### 1.12. Удалить все обработчики события 1903 | 1904 | Удалить все ранее утсановленные обработчики. 1905 | 1906 | ```php 1907 | public this removeAllListeners(string $event) 1908 | ``` 1909 | **$event** - событие 1910 | 1911 | **Примеры** 1912 | 1913 | ```php 1914 | $client->removeAllListeners('disk.downloaded'); 1915 | ``` 1916 | 1917 | ### 1.13. Обработчик события на основе класса 1918 | 1919 | Добавляет слушатель на основе класса. 1920 | 1921 | ```php 1922 | public this useListenerProvider(ListenerProviderInterface $provider) 1923 | ``` 1924 | 1925 | **$provider** - объект класса 1926 | 1927 | **Примеры** 1928 | 1929 | ```php 1930 | $client->useListenerProvider(new MyProvider); 1931 | ``` 1932 | 1933 | ### 1.14. Запустить событие 1934 | 1935 | Выполняет вызов слушателей события. 1936 | 1937 | ```php 1938 | public mixed emit(string $event) 1939 | ``` 1940 | 1941 | **$event** - событие 1942 | 1943 | **Примеры** 1944 | 1945 | ```php 1946 | $client->emit('custom.event', 'custom parameter', 'etc.'); 1947 | ``` 1948 | --------------------------------------------------------------------------------