示例#1
0
 /**
  *
  * @todo root path workaround
  * @todo partial copy
  *
  * @throws Exception
  * @param $update_list array[][string]string
  * @param $update_list []['source'] array[][string]string Source path or URI
  * @param $update_list []['target'] array[][string]string Target path
  * @param $update_list []['slug'] array[][string]string Update item slug (optional item identity)
  * @param $update_list []['md5'] array[][string]string MD5 of source archive (optional)
  *
  * @return array[][string]mixed
  * @return array[]['skipped']boolean
  * @return array[]['source']mixed
  * @return array[]['source']mixed
  * @return array[]['source']mixed
  * @return array[]['source']mixed
  * @return array[]['source']mixed
  * @return array[]['source']mixed
  */
 public function update($update_list)
 {
     $update_path = self::$update_path . $this->thread_id . '/';
     $download_path = $update_path . 'download/';
     try {
         $this->formatUpdateList($update_list, $update_path);
         $this->envSet();
         $resume = false;
         self::$ob_skip = false;
         //TODO write statistics into stage operations
         //TODO allow skip some update parts
         switch ($resume) {
             case false:
             case self::STAGE_FLUSH:
             case self::STAGE_NONE:
                 $this->setFullState(null);
             case self::STAGE_PREPARE:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['dependent']) {
                         $update['current_size'] = false;
                     } else {
                         $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                         $update['current_size'] = $this->run(self::STAGE_PREPARE, $update['pass'], $download_path, $update['extract_path'], $update['target']);
                     }
                 }
                 unset($update);
             case self::STAGE_COPY:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['skipped']) {
                         continue;
                     }
                     if (file_exists(self::$root_path . $update['target']) && !$update['dependent']) {
                         $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                         $result = $this->run(self::STAGE_COPY, $update['pass'], $update['target'], $update['extract_path'], $update['current_size']);
                         if (!$result && $update['pass']) {
                             $update['skipped'] = true;
                         }
                     }
                 }
                 unset($update);
             case self::STAGE_DOWNLOAD:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['skipped']) {
                         continue;
                     }
                     $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                     //TODO check name for $download_path
                     $update['archive'] = $this->run(self::STAGE_DOWNLOAD, $update['pass'], $update['source'], $download_path, isset($update['md5']) ? $update['md5'] : false);
                     if (!$update['archive'] && $update['pass']) {
                         $update['skipped'] = true;
                     }
                 }
                 unset($update);
             case self::STAGE_EXTRACT:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['skipped']) {
                         continue;
                     }
                     $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                     $result = $this->run(self::STAGE_EXTRACT, $update['pass'], $update['archive'], $update['extract_path'], $update['target']);
                     if (!$result && $update['pass']) {
                         $update['skipped'] = true;
                     }
                 }
                 unset($update);
             case self::STAGE_REPLACE:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['skipped'] || $update['dependent']) {
                         continue;
                     }
                     $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                     $update['backup'] = $this->run(self::STAGE_REPLACE, $update['pass'], $update['extract_path'], $update['target']);
                     if (!$update['backup'] && $update['pass']) {
                         $update['skipped'] = true;
                     }
                 }
                 unset($update);
             case self::STAGE_CLEANUP:
                 foreach ($update_list as $chunk_id => &$update) {
                     $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                     $paths = array();
                     $paths[$update_path] = true;
                     $cache_path = 'wa-cache/apps/' . ($this->current_chunk_id == 'wa-system' ? 'webasyst' : $this->current_chunk_id);
                     $paths[$cache_path] = true;
                     $this->run(self::STAGE_CLEANUP, false, $paths);
                 }
                 unset($update);
             case self::STAGE_VERIFY:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['skipped']) {
                         continue;
                     }
                     if (!isset($update['verify'])) {
                         continue;
                     }
                     if (strpos($update['target'], 'wa-config') !== false) {
                         $update['verify'] = false;
                     }
                     $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                     $this->run(self::STAGE_VERIFY, false, $update['target'], isset($update['verify']) && $update['verify']);
                 }
                 unset($update);
             case self::STAGE_UPDATE:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['skipped']) {
                         continue;
                     }
                     $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                     $this->run(self::STAGE_UPDATE, false);
                 }
                 unset($update);
                 break;
             default:
                 throw new Exception("Invalid resume state {$resume}");
                 break;
         }
         //$this->current_stage = 'update_'.self::STATE_COMPLETE;
         //$this->current_chunk_id = 'total';
         //$this->setState();
         $this->writeLog(__METHOD__, self::LOG_DEBUG, compact('update_list'));
         self::$ob_skip = true;
         $this->envReset();
         return $update_list;
     } catch (Exception $ex) {
         $this->cleanupPath($update_path, true);
         $this->writeLog($ex->getMessage(), self::LOG_WARNING, compact('update_list'));
         $this->envReset();
         self::$ob_skip = true;
         throw $ex;
     }
 }
 /**
  *
  * @todo root path workaround
  * @todo partial copy
  *
  * @throws Exception
  * @param $update_list <pre>array(
  * 		'source'=>uri|path string,
  * 		'md5'=>string,
  * 		'target'=>string,
  * )</pre>
  * @return array()
  */
 public function update($update_list)
 {
     try {
         $update_path = self::$update_path . $this->thread_id . '/';
         $download_path = $update_path . 'download/';
         $targets = array();
         foreach ($update_list as &$update) {
             $update['target'] = self::formatPath($update['target']) . '/';
             $update['target'] = preg_replace('@(^|/)\\.\\./@', '/', $update['target']);
             $update['extract_path'] = $update_path . 'update/' . $update['target'];
             if (!isset($update['pass'])) {
                 $update['pass'] = false;
             }
             if (!isset($update['skipped'])) {
                 $update['skipped'] = false;
             }
             $founded = false;
             foreach ($targets as $id => $target) {
                 if (strpos($target, $update['target']) === 0) {
                     $founded = true;
                     if (strlen($target) > strlen($update['target'])) {
                         $targets[$id] = $update['target'];
                     }
                     break;
                 } elseif (strpos($update['target'], $target) === 0) {
                     $founded = true;
                     break;
                 }
             }
             if (!$founded) {
                 $targets[] = $update['target'];
             }
             unset($update);
         }
         foreach ($update_list as &$update) {
             $update['dependent'] = false;
             foreach ($targets as $id => $target) {
                 if (strpos($update['target'], $target) === 0) {
                     if (strlen($target) < strlen($update['target'])) {
                         $update['dependent'] = true;
                     }
                     break;
                 }
             }
             unset($update);
         }
         $this->writeLog(__METHOD__ . ' tree', self::LOG_DEBUG, array('targets' => $targets, 'update_list' => $update_list));
         #sort
         uasort($update_list, array(__CLASS__, 'sortUpdateList'));
         $session_id = session_id();
         if ($session_id) {
             if (function_exists('wa') && method_exists($wa = wa(), 'getStorage')) {
                 $wa->getStorage()->close();
             } else {
                 session_write_close();
             }
         }
         if ($this->log_level >= self::LOG_DEBUG) {
             $this->writeLog('callback', self::LOG_DEBUG, self::debug_backtrace_custom());
         }
         $error_level = error_reporting();
         $display_errors = ini_get('display_errors');
         $error_reporting = ini_get('error_reporting');
         @ini_set('display_errors', true);
         @ini_set('error_reporting', E_ALL & ~E_NOTICE);
         error_reporting(E_ALL & ~E_NOTICE);
         ignore_user_abort(true);
         $this->writeLog('Register error handler', self::LOG_TRACE, ob_start(__CLASS__ . '::obHandler'));
         self::$ob_skip = false;
         $resume = false;
         //TODO write statistics into stage operations
         //TODO allow skip some update parts
         switch ($resume) {
             case false:
             case self::STAGE_FLUSH:
             case self::STAGE_NONE:
                 $this->setFullState(null);
             case self::STAGE_PREPARE:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['dependent']) {
                         $update['current_size'] = false;
                     } else {
                         $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                         $update['current_size'] = $this->run(self::STAGE_PREPARE, $update['pass'], $download_path, $update['extract_path'], $update['target']);
                     }
                 }
                 unset($update);
             case self::STAGE_COPY:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['skipped']) {
                         continue;
                     }
                     if (file_exists(self::$root_path . $update['target']) && !$update['dependent']) {
                         $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                         $result = $this->run(self::STAGE_COPY, $update['pass'], $update['target'], $update['extract_path'], $update['current_size']);
                         if (!$result && $update['pass']) {
                             $update['skipped'] = true;
                         }
                     }
                 }
                 unset($update);
             case self::STAGE_DOWNLOAD:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['skipped']) {
                         continue;
                     }
                     $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                     //TODO check name for $download_path
                     $update['archive'] = $this->run(self::STAGE_DOWNLOAD, $update['pass'], $update['source'], $download_path, isset($update['md5']) ? $update['md5'] : false);
                     if (!$update['archive'] && $update['pass']) {
                         $update['skipped'] = true;
                     }
                 }
                 unset($update);
             case self::STAGE_EXTRACT:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['skipped']) {
                         continue;
                     }
                     $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                     $result = $this->run(self::STAGE_EXTRACT, $update['pass'], $update['archive'], $update['extract_path'], $update['target']);
                     if (!$result && $update['pass']) {
                         $update['skipped'] = true;
                     }
                 }
                 unset($update);
             case self::STAGE_REPLACE:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['skipped'] || $update['dependent']) {
                         continue;
                     }
                     $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                     $update['backup'] = $this->run(self::STAGE_REPLACE, $update['pass'], $update['extract_path'], $update['target']);
                     if (!$update['backup'] && $update['pass']) {
                         $update['skipped'] = true;
                     }
                 }
                 unset($update);
             case self::STAGE_CLEANUP:
                 foreach ($update_list as $chunk_id => &$update) {
                     $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                     $paths = array();
                     $paths[$update_path] = true;
                     $cache_path = 'wa-cache/apps/' . ($this->current_chunk_id == 'wa-system' ? 'webasyst' : $this->current_chunk_id);
                     $paths[$cache_path] = true;
                     $this->run(self::STAGE_CLEANUP, false, $paths);
                 }
                 unset($update);
             case self::STAGE_VERIFY:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['skipped']) {
                         continue;
                     }
                     if (!isset($update['verify'])) {
                         continue;
                     }
                     if (strpos($update['target'], 'wa-config') !== false) {
                         $update['verify'] = false;
                     }
                     $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                     $this->run(self::STAGE_VERIFY, false, $update['target'], isset($update['verify']) && $update['verify']);
                 }
                 unset($update);
             case self::STAGE_UPDATE:
                 foreach ($update_list as $chunk_id => &$update) {
                     if ($update['skipped']) {
                         continue;
                     }
                     $this->current_chunk_id = isset($update['slug']) ? $update['slug'] : $chunk_id;
                     $this->run(self::STAGE_UPDATE, false);
                 }
                 unset($update);
                 break;
             default:
                 throw new Exception("Invalid resume state {$resume}");
                 break;
         }
         //$this->current_stage = 'update_'.self::STATE_COMPLETE;
         //$this->current_chunk_id = 'total';
         //$this->setState();
         $this->writeLog(__METHOD__, self::LOG_DEBUG, array('source' => $update_list));
         self::$ob_skip = true;
         error_reporting($error_level);
         @ini_set('display_errors', $display_errors);
         @ini_set('error_reporting', $error_reporting);
         if ($session_id) {
             if ($wa) {
                 $wa->getStorage()->open();
             } else {
                 session_start();
             }
         }
         return $update_list;
     } catch (Exception $ex) {
         $this->cleanupPath($update_path, true);
         $this->writeLog($ex->getMessage(), self::LOG_WARNING, array('source' => $update_list));
         if ($session_id) {
             if ($wa) {
                 $wa->getStorage()->open();
             } else {
                 session_start();
             }
         }
         self::$ob_skip = true;
         throw $ex;
     }
 }