Overview

Namespaces

  • OpenCloud
    • Autoscale
      • Resource
    • CDN
      • Resource
    • CloudMonitoring
      • Collection
      • 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
      • Collection
      • Enum
      • Resource
    • Networking
      • Resource
    • ObjectStore
      • Constants
      • Enum
      • Exception
      • Resource
      • Upload
    • Orchestration
      • Resource
    • Queues
      • Collection
      • Exception
      • Resource
    • Volume
      • Resource
  • PHP

Classes

  • AbstractTransfer
  • ConcurrentTransfer
  • ConsecutiveTransfer
  • ContainerMigration
  • DirectorySync
  • TransferBuilder
  • TransferPart
  • TransferState
  • Overview
  • Namespace
  • Class
  • Tree
  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:             $callback = $this->getCallback($filename);
116:             $filePath = rtrim($this->basePath, '/') . '/' . $filename;
117: 
118:             if (!is_readable($filePath)) {
119:                 continue;
120:             }
121: 
122:             $entities[] = $entityBody = EntityBody::factory(fopen($filePath, 'r+'));
123: 
124:             if (false !== ($remoteFile = $this->remoteFiles->search($callback))) {
125:                 // if different, upload updated version
126:                 if ($remoteFile->getEtag() != $entityBody->getContentMd5()) {
127:                     $requests[] = $this->container->getClient()->put(
128:                         $remoteFile->getUrl(),
129:                         $remoteFile->getMetadata()->toArray(),
130:                         $entityBody
131:                     );
132:                 }
133:             } else {
134:                 // upload new file
135:                 $url = clone $this->container->getUrl();
136:                 $url->addPath($filename);
137: 
138:                 $requests[] = $this->container->getClient()->put($url, array(), $entityBody);
139:             }
140:         }
141: 
142:         // Handle DELETE requests
143:         foreach ($this->remoteFiles as $remoteFile) {
144:             $remoteName = $remoteFile->getName();
145:             if (!in_array($remoteName, $localFiles)) {
146:                 $deletePaths[] = sprintf('/%s/%s', $this->container->getName(), $remoteName);
147:             }
148:         }
149: 
150:         // send update/create requests
151:         if (count($requests)) {
152:             $this->container->getClient()->send($requests);
153:         }
154: 
155:         // bulk delete
156:         if (count($deletePaths)) {
157:             $this->container->getService()->bulkDelete($deletePaths);
158:         }
159: 
160:         // close all streams
161:         if (count($entities)) {
162:             foreach ($entities as $entity) {
163:                 $entity->close();
164:             }
165:         }
166:     }
167: 
168:     /**
169:      * Given a path, traverse it recursively for nested files.
170:      *
171:      * @param $path
172:      * @return array
173:      */
174:     private function traversePath($path)
175:     {
176:         $filenames = array();
177: 
178:         $directory = new DirectoryIterator($path);
179: 
180:         foreach ($directory as $file) {
181:             if ($file->isDot()) {
182:                 continue;
183:             }
184:             if ($file->isDir()) {
185:                 $filenames = array_merge($filenames, $this->traversePath($file->getPathname()));
186:             } else {
187:                 $filenames[] = $this->trimFilename($file);
188:             }
189:         }
190: 
191:         return $filenames;
192:     }
193: 
194:     /**
195:      * Given a path, trim away leading slashes and strip the base path.
196:      *
197:      * @param $file
198:      * @return string
199:      */
200:     private function trimFilename($file)
201:     {
202:         return ltrim(str_replace($this->basePath, '', $file->getPathname()), '/');
203:     }
204: 
205:     /**
206:      * Get the callback used to do a search function on the remote iterator.
207:      *
208:      * @param $name     The name of the file we're looking for.
209:      * @return callable
210:      */
211:     private function getCallback($name)
212:     {
213:         $name = trim($name, '/');
214: 
215:         return function ($remoteFile) use ($name) {
216:             if ($remoteFile->getName() == $name) {
217:                 return true;
218:             }
219: 
220:             return false;
221:         };
222:     }
223: }
224: 
API documentation generated by ApiGen 2.8.0