/**
  * Resolve any dependencies of the artifact represented in this vehicle.
  *
  * @param xPDOTransport &$transport A reference to the xPDOTransport in
  * which this vehicle is stored.
  * @param mixed &$object An object reference to resolve dependencies for.
  * Use this to make the artifact or other important data available to the
  * resolver scripts.
  * @param array $options Additional options for the resolution process.
  * @return boolean Indicates if the resolution was successful.
  */
 public function resolve(&$transport, &$object, $options = array())
 {
     $resolved = false;
     if (isset($this->payload['resolve'])) {
         while (list($rKey, $r) = each($this->payload['resolve'])) {
             $type = $r['type'];
             $body = $r['body'];
             $preExistingMode = xPDOTransport::PRESERVE_PREEXISTING;
             if (!empty($options[xPDOTransport::PREEXISTING_MODE])) {
                 $preExistingMode = intval($options[xPDOTransport::PREEXISTING_MODE]);
             }
             switch ($type) {
                 case 'file':
                     if (isset($options[xPDOTransport::RESOLVE_FILES]) && !$options[xPDOTransport::RESOLVE_FILES]) {
                         $resolved = true;
                         continue;
                     }
                     if ($transport->xpdo->getDebug() === true) {
                         $transport->xpdo->log(xPDO::LOG_LEVEL_DEBUG, "Resolving transport files: " . print_r($this, true));
                     }
                     $fileMeta = $transport->xpdo->fromJSON($body, true);
                     $fileName = $fileMeta['name'];
                     $fileSource = $transport->path . $fileMeta['source'];
                     $fileTarget = eval($fileMeta['target']);
                     $fileTargetPath = $fileTarget . $fileName;
                     $preservedArchive = $transport->path . $transport->signature . '/' . $this->payload['class'] . '/' . $this->payload['signature'] . '.' . $rKey . '.preserved.zip';
                     $cacheManager = $transport->xpdo->getCacheManager();
                     switch ($options[xPDOTransport::PACKAGE_ACTION]) {
                         case xPDOTransport::ACTION_UPGRADE:
                         case xPDOTransport::ACTION_INSTALL:
                             // if package is installing
                             if ($cacheManager && file_exists($fileSource) && !empty($fileTarget)) {
                                 $copied = array();
                                 if ($preExistingMode === xPDOTransport::PRESERVE_PREEXISTING && file_exists($fileTargetPath)) {
                                     $transport->xpdo->log(xPDO::LOG_LEVEL_INFO, "Attempting to preserve files at {$fileTargetPath} into archive {$preservedArchive}");
                                     $preserved = xPDOTransport::_pack($transport->xpdo, $preservedArchive, $fileTarget, $fileName);
                                 }
                                 if (is_dir($fileSource)) {
                                     $copied = $cacheManager->copyTree($fileSource, $fileTarget, array_merge($options, array('copy_return_file_stat' => true)));
                                 } elseif (is_file($fileSource)) {
                                     $copied = $cacheManager->copyFile($fileSource, $fileTarget, array_merge($options, array('copy_return_file_stat' => true)));
                                 }
                                 if (empty($copied)) {
                                     $transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not copy {$fileSource} to {$fileTargetPath}");
                                 } else {
                                     if ($preExistingMode === xPDOTransport::PRESERVE_PREEXISTING && is_array($copied)) {
                                         foreach ($copied as $copiedFile => $stat) {
                                             if (isset($stat['overwritten'])) {
                                                 $transport->_preserved[$options['guid']]['files'][substr($copiedFile, strlen($fileTarget))] = $stat;
                                             }
                                         }
                                     }
                                     $resolved = true;
                                 }
                             } else {
                                 $transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not copy {$fileSource} to {$fileTargetPath}");
                             }
                             break;
                         case xPDOTransport::ACTION_UNINSTALL:
                             /* if package is uninstalling
                                user can override whether or not files from resolver are removed
                                however default action is to remove */
                             if (!isset($options[xPDOTransport::RESOLVE_FILES_REMOVE]) || $options[xPDOTransport::RESOLVE_FILES_REMOVE] !== false) {
                                 $path = $fileTarget . $fileName;
                                 $transport->xpdo->log(xPDO::LOG_LEVEL_INFO, 'Removing files in file resolver: ' . $path);
                                 if ($cacheManager && file_exists($path)) {
                                     if (is_dir($path) && $cacheManager->deleteTree($path, array_merge(array('deleteTop' => true, 'skipDirs' => false, 'extensions' => array()), $options))) {
                                         $resolved = true;
                                     } elseif (is_file($path) && unlink($path)) {
                                         $resolved = true;
                                     } else {
                                         $transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'Could not remove files from path: ' . $path);
                                     }
                                 } else {
                                     $transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'Could not find files to remove.');
                                 }
                             } else {
                                 /* action was chosen not to remove, send log message and continue */
                                 $transport->xpdo->log(xPDO::LOG_LEVEL_INFO, 'Skipping removing of files according to vehicle attributes.');
                                 $resolved = true;
                             }
                             if ($preExistingMode === xPDOTransport::RESTORE_PREEXISTING && file_exists($preservedArchive)) {
                                 $transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Attempting to restore files to {$fileTarget} from archive {$preservedArchive}");
                                 $unpackedResult = xPDOTransport::_unpack($transport->xpdo, $preservedArchive, $fileTarget);
                                 if ($unpackedResult > 0) {
                                     $resolved = true;
                                 } else {
                                     $transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Error unpacking preserved files from archive {$preservedArchive}");
                                 }
                             }
                             break;
                     }
                     break;
                 case 'php':
                     if (isset($options[xPDOTransport::RESOLVE_PHP]) && !$options[xPDOTransport::RESOLVE_PHP]) {
                         continue;
                     }
                     $fileMeta = $transport->xpdo->fromJSON($body, true);
                     $fileName = $fileMeta['name'];
                     $fileSource = $transport->path . $fileMeta['source'];
                     if (!($resolved = (include $fileSource))) {
                         $transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, "xPDOVehicle resolver failed: type php ({$fileSource})");
                     }
                     break;
                 default:
                     $transport->xpdo->log(xPDO::LOG_LEVEL_WARN, "xPDOVehicle does not support resolvers of type {$type}.");
                     break;
             }
         }
     } else {
         $resolved = true;
     }
     return $resolved;
 }
 /**
  * Uninstall files or folders represented by and stored in this vehicle.
  *
  * @access protected
  * @param xPDOTransport &$transport A reference the transport this vehicle is stored in.
  * @param array $options Optional attributes that can be applied to vehicle uninstall process.
  * @return boolean True if the files are uninstalled successfully.
  */
 protected function _uninstallFiles(&$transport, $options)
 {
     $uninstalled = false;
     $vOptions = $this->get($transport, $options);
     if (isset($vOptions['object']) && isset($vOptions['object']['source']) && isset($vOptions['object']['target'])) {
         $object = $vOptions['object'];
         $fileName = $object['name'];
         $fileSource = $transport->path . $object['source'];
         $fileTarget = eval($object['target']);
         $preExistingMode = xPDOTransport::PRESERVE_PREEXISTING;
         if (isset($vOptions[xPDOTransport::PREEXISTING_MODE])) {
             $preExistingMode = (int) $vOptions[xPDOTransport::PREEXISTING_MODE];
         }
         $cacheManager = $transport->xpdo->getCacheManager();
         $path = $fileTarget . $fileName;
         $transport->xpdo->log(xPDO::LOG_LEVEL_INFO, 'Uninstalling files from xPDOFileVehicle: ' . $path);
         if ($this->validate($transport, $object, $vOptions)) {
             if (!isset($vOptions[xPDOTransport::UNINSTALL_FILES]) || $vOptions[xPDOTransport::UNINSTALL_FILES] == true) {
                 $transport->xpdo->log(xPDO::LOG_LEVEL_INFO, 'Removing files from xPDOFileVehicle: ' . $path);
                 if ($cacheManager && file_exists($path)) {
                     if (is_dir($path) && $cacheManager->deleteTree($path, array_merge(array('deleteTop' => true, 'skipDirs' => false, 'extensions' => array()), $vOptions))) {
                         $uninstalled = true;
                     } elseif (is_file($path) && unlink($path)) {
                         $uninstalled = true;
                     } else {
                         $transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'Could not remove files from path: ' . $path);
                     }
                 } else {
                     $transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'Could not find files to remove at path: ' . $path);
                 }
             } else {
                 $transport->xpdo->log(xPDO::LOG_LEVEL_INFO, 'Skipping removal of files according to vehicle attributes.');
                 $uninstalled = true;
             }
             $preservedArchive = $transport->path . $transport->signature . '/' . $this->payload['class'] . '/' . $this->payload['signature'] . '.preserved.zip';
             if ($preExistingMode === xPDOTransport::RESTORE_PREEXISTING && file_exists($preservedArchive)) {
                 $transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Attempting to restore files to {$fileTarget} from archive {$preservedArchive}");
                 $unpackedResult = xPDOTransport::_unpack($transport->xpdo, $preservedArchive, $fileTarget);
                 if ($unpackedResult > 0) {
                     $uninstalled = true;
                 } else {
                     $transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Error unpacking preserved files from archive {$preservedArchive}");
                 }
             }
             if (!$this->resolve($transport, $object, $vOptions)) {
                 $transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'Could not resolve vehicle for object: ' . print_r($object, true));
                 if ($transport->xpdo->getDebug() === true) {
                     $transport->xpdo->log(xPDO::LOG_LEVEL_DEBUG, 'Could not resolve vehicle: ' . print_r($vOptions, true));
                 }
             }
         } else {
             //$transport->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'Could not validate vehicle for object: ' . print_r($object, true));
             if ($transport->xpdo->getDebug() === true) {
                 $transport->xpdo->log(xPDO::LOG_LEVEL_DEBUG, 'Could not validate vehicle: ' . print_r($vOptions, true));
             }
         }
     }
     return $uninstalled;
 }
 /**
  * Unpack the package to prepare for installation and return a manifest.
  *
  * @param xPDO &$xpdo A reference to an xPDO instance.
  * @param string $from Filename of the archive containing the transport package.
  * @param string $to The root path where the contents of the archive should be extracted.  This
  * path must be writable by the user executing the PHP process on the server.
  * @param integer $state The current state of the package, i.e. packed or unpacked.
  * @return array The manifest which is included after successful extraction.
  */
 public static function unpack(&$xpdo, $from, $to, $state = xPDOTransport::STATE_PACKED)
 {
     $manifest = null;
     if ($state !== xPDOTransport::STATE_UNPACKED) {
         $resources = xPDOTransport::_unpack($xpdo, $from, $to);
     } else {
         $resources = true;
     }
     if ($resources) {
         $manifestFilename = $to . basename($from, '.transport.zip') . '/manifest.php';
         if (file_exists($manifestFilename)) {
             $manifest = @(include $manifestFilename);
         } else {
             $xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not find package manifest at {$manifestFilename}");
         }
     }
     return $manifest;
 }