├── 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 |
--------------------------------------------------------------------------------