Overview

Namespaces

  • OpenCloud
    • Autoscale
      • Resource
    • CloudMonitoring
      • Exception
      • Resource
    • Common
      • Collection
      • Constants
      • Exceptions
      • Http
        • Message
      • Log
      • Resource
      • Service
    • Compute
      • Constants
      • Exception
      • Resource
    • Database
      • Resource
    • DNS
      • Collection
      • Resource
    • Identity
      • Constants
      • Resource
    • Image
      • Enum
      • Resource
        • JsonPatch
        • Schema
    • LoadBalancer
      • Enum
      • Resource
    • ObjectStore
      • Constants
      • Exception
      • Resource
      • Upload
    • Orchestration
    • Queues
      • Exception
      • Resource
    • Volume
      • Resource
  • PHP

Classes

  • AbstractTransfer
  • ConcurrentTransfer
  • ConsecutiveTransfer
  • ContainerMigration
  • DirectorySync
  • TransferBuilder
  • TransferPart
  • TransferState
  • Overview
  • Namespace
  • Class
  • Tree
  • Download
  1: <?php
  2: /**
  3:  * Copyright 2012-2014 Rackspace US, Inc.
  4:  *
  5:  * Licensed under the Apache License, Version 2.0 (the "License");
  6:  * you may not use this file except in compliance with the License.
  7:  * You may obtain a copy of the License at
  8:  *
  9:  * http://www.apache.org/licenses/LICENSE-2.0
 10:  *
 11:  * Unless required by applicable law or agreed to in writing, software
 12:  * distributed under the License is distributed on an "AS IS" BASIS,
 13:  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14:  * See the License for the specific language governing permissions and
 15:  * limitations under the License.
 16:  */
 17: 
 18: namespace OpenCloud\ObjectStore\Upload;
 19: 
 20: use DirectoryIterator;
 21: use Guzzle\Http\EntityBody;
 22: use OpenCloud\Common\Collection\ResourceIterator;
 23: use OpenCloud\Common\Exceptions\InvalidArgumentError;
 24: use OpenCloud\ObjectStore\Resource\Container;
 25: 
 26: /**
 27:  * DirectorySync upload class, in charge of creating, replacing and delete data objects on the API. The goal of
 28:  * this execution is to sync local directories with remote CloudFiles containers so that they are consistent.
 29:  *
 30:  * @package OpenCloud\ObjectStore\Upload
 31:  */
 32: class DirectorySync
 33: {
 34:     /**
 35:      * @var string The path to the directory you're syncing.
 36:      */
 37:     private $basePath;
 38:     /**
 39:      * @var ResourceIterator A collection of remote files in Swift.
 40:      */
 41:     private $remoteFiles;
 42:     /**
 43:      * @var AbstractContainer The Container object you are syncing.
 44:      */
 45:     private $container;
 46: 
 47:     /**
 48:      * Basic factory method to instantiate a new DirectorySync object with all the appropriate properties.
 49:      *
 50:      * @param           $path      The local path
 51:      * @param Container $container The container you're syncing
 52:      * @return DirectorySync
 53:      */
 54:     public static function factory($path, Container $container)
 55:     {
 56:         $transfer = new self();
 57:         $transfer->setBasePath($path);
 58:         $transfer->setContainer($container);
 59:         $transfer->setRemoteFiles($container->objectList());
 60: 
 61:         return $transfer;
 62:     }
 63: 
 64:     /**
 65:      * @param $path
 66:      * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
 67:      */
 68:     public function setBasePath($path)
 69:     {
 70:         if (!file_exists($path)) {
 71:             throw new InvalidArgumentError(sprintf('%s does not exist', $path));
 72:         }
 73: 
 74:         $this->basePath = $path;
 75:     }
 76: 
 77:     /**
 78:      * @param ResourceIterator $remoteFiles
 79:      */
 80:     public function setRemoteFiles(ResourceIterator $remoteFiles)
 81:     {
 82:         $this->remoteFiles = $remoteFiles;
 83:     }
 84: 
 85:     /**
 86:      * @param Container $container
 87:      */
 88:     public function setContainer(Container $container)
 89:     {
 90:         $this->container = $container;
 91:     }
 92: 
 93:     /**
 94:      * Execute the sync process. This will collect all the remote files from the API and do a comparison. There are
 95:      * four scenarios that need to be dealt with:
 96:      *
 97:      * - Exists locally, exists remotely (identical checksum) = no action
 98:      * - Exists locally, exists remotely (diff checksum) = local overwrites remote
 99:      * - Exists locally, not exists remotely = local is written to remote
100:      * - Not exists locally, exists remotely = remote file is deleted
101:      */
102:     public function execute()
103:     {
104:         $localFiles = $this->traversePath($this->basePath);
105: 
106:         $this->remoteFiles->rewind();
107:         $this->remoteFiles->populateAll();
108: 
109:         $entities = array();
110:         $requests = array();
111:         $deletePaths = array();
112: 
113:         // Handle PUT requests (create/update files)
114:         foreach ($localFiles as $filename) {
115: 
116:             $callback = $this->getCallback($filename);
117:             $filePath = rtrim($this->basePath, '/') . '/' . $filename;
118: 
119:             if (!is_readable($filePath)) {
120:                 continue;
121:             }
122: 
123:             $entities[] = $entityBody = EntityBody::factory(fopen($filePath, 'r+'));
124: 
125:             if (false !== ($remoteFile = $this->remoteFiles->search($callback))) {
126:                 // if different, upload updated version
127:                 if ($remoteFile->getEtag() != $entityBody->getContentMd5()) {
128:                     $requests[] = $this->container->getClient()->put(
129:                         $remoteFile->getUrl(),
130:                         $remoteFile->getMetadata()->toArray(),
131:                         $entityBody
132:                     );
133:                 }
134:             } else {
135:                 // upload new file
136:                 $url = clone $this->container->getUrl();
137:                 $url->addPath($filename);
138: 
139:                 $requests[] = $this->container->getClient()->put($url, array(), $entityBody);
140:             }
141:         }
142: 
143:         // Handle DELETE requests
144:         foreach ($this->remoteFiles as $remoteFile) {
145:             $remoteName = $remoteFile->getName();
146:             if (!in_array($remoteName, $localFiles)) {
147:                 $deletePaths[] = sprintf('/%s/%s', $this->container->getName(), $remoteName);
148:             }
149:         }
150: 
151:         // send update/create requests
152:         if (count($requests)) {
153:             $this->container->getClient()->send($requests);
154:         }
155: 
156:         // bulk delete
157:         if (count($deletePaths)) {
158:             $this->container->getService()->bulkDelete($deletePaths);
159:         }
160: 
161:         // close all streams
162:         if (count($entities)) {
163:             foreach ($entities as $entity) {
164:                 $entity->close();
165:             }
166:         }
167:     }
168: 
169:     /**
170:      * Given a path, traverse it recursively for nested files.
171:      *
172:      * @param $path
173:      * @return array
174:      */
175:     private function traversePath($path)
176:     {
177:         $filenames = array();
178: 
179:         $directory = new DirectoryIterator($path);
180: 
181:         foreach ($directory as $file) {
182:             if ($file->isDot()) {
183:                 continue;
184:             }
185:             if ($file->isDir()) {
186:                 $filenames = array_merge($filenames, $this->traversePath($file->getPathname()));
187:             } else {
188:                 $filenames[] = $this->trimFilename($file);
189:             }
190:         }
191: 
192:         return $filenames;
193:     }
194: 
195:     /**
196:      * Given a path, trim away leading slashes and strip the base path.
197:      *
198:      * @param $file
199:      * @return string
200:      */
201:     private function trimFilename($file)
202:     {
203:         return ltrim(str_replace($this->basePath, '', $file->getPathname()), '/');
204:     }
205: 
206:     /**
207:      * Get the callback used to do a search function on the remote iterator.
208:      *
209:      * @param $name     The name of the file we're looking for.
210:      * @return callable
211:      */
212:     private function getCallback($name)
213:     {
214:         $name = trim($name, '/');
215: 
216:         return function ($remoteFile) use ($name) {
217:             if ($remoteFile->getName() == $name) {
218:                 return true;
219:             }
220: 
221:             return false;
222:         };
223:     }
224: }
225: 
PHP OpenCloud API API documentation generated by ApiGen 2.8.0