?iť?
Current Path : /home/scgforma/www/cloud/3rdparty/rackspace/php-opencloud/lib/OpenCloud/ObjectStore/Upload/ |
Current File : /home/scgforma/www/cloud/3rdparty/rackspace/php-opencloud/lib/OpenCloud/ObjectStore/Upload/DirectorySync.php |
<?php /** * Copyright 2012-2014 Rackspace US, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ namespace OpenCloud\ObjectStore\Upload; use DirectoryIterator; use Guzzle\Http\EntityBody; use OpenCloud\Common\Collection\ResourceIterator; use OpenCloud\Common\Exceptions\InvalidArgumentError; use OpenCloud\ObjectStore\Resource\Container; /** * DirectorySync upload class, in charge of creating, replacing and delete data objects on the API. The goal of * this execution is to sync local directories with remote CloudFiles containers so that they are consistent. * * @package OpenCloud\ObjectStore\Upload */ class DirectorySync { /** @var string The path to the directory you're syncing. */ private $basePath; /** @var ResourceIterator A collection of remote files in Swift. */ private $remoteFiles; /** @var AbstractContainer The Container object you are syncing. */ private $container; /** @var string */ private $targetDir; /** * Basic factory method to instantiate a new DirectorySync object with all the appropriate properties. * * @param string $path The local path * @param Container $container The container you're syncing * @param string $targetDir The path (or pseudo-directory) that the files will be nested in * * @return DirectorySync */ public static function factory($path, Container $container, $targetDir = null) { $transfer = new self(); $transfer->setBasePath($path); $transfer->setContainer($container); $transfer->setRemoteFiles($container->objectList()); $transfer->setTargetDir($targetDir); return $transfer; } /** * @param $path * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError */ public function setBasePath($path) { if (!file_exists($path)) { throw new InvalidArgumentError(sprintf('%s does not exist', $path)); } $this->basePath = $path; } /** * @param ResourceIterator $remoteFiles */ public function setRemoteFiles(ResourceIterator $remoteFiles) { $this->remoteFiles = $remoteFiles; } /** * @param Container $container */ public function setContainer(Container $container) { $this->container = $container; } /** * @param string $dir The target path that all files will be nested in. By default, the files will not be nested. */ public function setTargetDir($dir) { $this->targetDir = rtrim($dir, '/'); } /** * Execute the sync process. This will collect all the remote files from the API and do a comparison. There are * four scenarios that need to be dealt with: * * - Exists locally, exists remotely (identical checksum) = no action * - Exists locally, exists remotely (diff checksum) = local overwrites remote * - Exists locally, not exists remotely = local is written to remote * - Not exists locally, exists remotely = remote file is deleted */ public function execute() { $localFiles = $this->traversePath($this->basePath); $this->remoteFiles->rewind(); $this->remoteFiles->populateAll(); $entities = array(); $requests = array(); $deletePaths = array(); // Handle PUT requests (create/update files) foreach ($localFiles as $filename) { $remoteFilename = $this->targetDir ? $this->targetDir . '/' . $filename : $filename; $callback = $this->getCallback($remoteFilename); $filePath = rtrim($this->basePath, '/') . '/' . $filename; if (!is_readable($filePath)) { continue; } $entities[] = $entityBody = EntityBody::factory(fopen($filePath, 'r+')); if (false !== ($remoteFile = $this->remoteFiles->search($callback))) { // if different, upload updated version if ($remoteFile->getEtag() != $entityBody->getContentMd5()) { $requests[] = $this->container->getClient()->put( $remoteFile->getUrl(), $remoteFile->getMetadata()->toArray(), $entityBody ); } } else { // upload new file $url = clone $this->container->getUrl(); $url->addPath($remoteFilename); $requests[] = $this->container->getClient()->put($url, array(), $entityBody); } } // Handle DELETE requests foreach ($this->remoteFiles as $remoteFile) { $remoteName = $remoteFile->getName(); if (!in_array($remoteName, $localFiles)) { $deletePaths[] = sprintf('/%s/%s', $this->container->getName(), $remoteName); } } // send update/create requests if (count($requests)) { $this->container->getClient()->send($requests); } // bulk delete if (count($deletePaths)) { $this->container->getService()->bulkDelete($deletePaths); } // close all streams if (count($entities)) { foreach ($entities as $entity) { $entity->close(); } } } /** * Given a path, traverse it recursively for nested files. * * @param $path * @return array */ private function traversePath($path) { $filenames = array(); $directory = new DirectoryIterator($path); foreach ($directory as $file) { if ($file->isDot()) { continue; } if ($file->isDir()) { $filenames = array_merge($filenames, $this->traversePath($file->getPathname())); } else { $filenames[] = $this->trimFilename($file); } } return $filenames; } /** * Given a path, trim away leading slashes and strip the base path. * * @param $file * @return string */ private function trimFilename($file) { return ltrim(str_replace($this->basePath, '', $file->getPathname()), '/'); } /** * Get the callback used to do a search function on the remote iterator. * * @param $name The name of the file we're looking for. * @return callable */ private function getCallback($name) { $name = trim($name, '/'); return function ($remoteFile) use ($name) { if ($remoteFile->getName() == $name) { return true; } return false; }; } }