<commit_before><?php

/**
 * Upload a file to Phabricator.
 */
final class ArcanistUploadWorkflow extends ArcanistWorkflow {

  private $paths;
  private $json;

  public function getWorkflowName() {
    return 'upload';
  }

  public function getCommandSynopses() {
    return phutil_console_format(<<<EOTEXT
      **upload** __file__ [__file__ ...] [--json]
EOTEXT
      );
  }

  public function getCommandHelp() {
    return phutil_console_format(<<<EOTEXT
          Supports: filesystems
          Upload a file from local disk.
EOTEXT
      );
  }

  public function getArguments() {
    return array(
      'json' => array(
        'help' => pht('Output upload information in JSON format.'),
      ),
      '*' => 'paths',
    );
  }

  protected function didParseArguments() {
    if (!$this->getArgument('paths')) {
      throw new ArcanistUsageException(
        pht('Specify one or more files to upload.'));
    }

    $this->paths = $this->getArgument('paths');
    $this->json = $this->getArgument('json');
  }

  public function requiresAuthentication() {
    return true;
  }

  public function run() {
    $conduit = $this->getConduit();
    $results = array();

    foreach ($this->paths as $path) {
      $path = Filesystem::resolvePath($path);

      $name = basename($path);
      $this->writeStatus(pht("Uploading '%s'...", $name));

      $hash = @sha1_file($path);
      if (!$hash) {
        throw new Exception(pht('Unable to read file "%s"!', $path));
      }
      $length = filesize($path);

      $phid = null;
      try {
        $result = $conduit->callMethodSynchronous(
          'file.allocate',
          array(
            'name' => $name,
            'contentLength' => $length,
            'contentHash' => $hash,
          ));

        $phid = $result['filePHID'];
        if (!$result['upload']) {
          if (!$phid) {
            $this->writeStatus(
              pht(
                'Unable to upload file "%s": the server refused to accept '.
                'it. This usually means it is too large.',
                $name));
            continue;
          }
          // Otherwise, the server completed the upload by referencing known
          // file data.
        } else {
          if ($phid) {
            $this->uploadChunks($phid, $path);
          } else {
            // This is a small file that doesn't need to be uploaded in
            // chunks, so continue normally.
          }
        }
      } catch (Exception $ex) {
        $this->writeStatus(
          pht('Unable to use allocate method, trying older upload method.'));
      }

      if (!$phid) {
        try {
          $data = Filesystem::readFile($path);
        } catch (FilesystemException $ex) {
          $this->writeStatus(
            pht('Unable to read file "%s".', $ex->getMessage()));
          $results[$path] = null;
          continue;
        }

        $phid = $conduit->callMethodSynchronous(
          'file.upload',
          array(<commit_msg>Fix chunk upload fallback behavior<commit_after><?php

/**
 * Upload a file to Phabricator.
 */
final class ArcanistUploadWorkflow extends ArcanistWorkflow {

  private $paths;
  private $json;

  public function getWorkflowName() {
    return 'upload';
  }

  public function getCommandSynopses() {
    return phutil_console_format(<<<EOTEXT
      **upload** __file__ [__file__ ...] [--json]
EOTEXT
      );
  }

  public function getCommandHelp() {
    return phutil_console_format(<<<EOTEXT
          Supports: filesystems
          Upload a file from local disk.
EOTEXT
      );
  }

  public function getArguments() {
    return array(
      'json' => array(
        'help' => pht('Output upload information in JSON format.'),
      ),
      '*' => 'paths',
    );
  }

  protected function didParseArguments() {
    if (!$this->getArgument('paths')) {
      throw new ArcanistUsageException(
        pht('Specify one or more files to upload.'));
    }

    $this->paths = $this->getArgument('paths');
    $this->json = $this->getArgument('json');
  }

  public function requiresAuthentication() {
    return true;
  }

  public function run() {
    $conduit = $this->getConduit();
    $results = array();

    foreach ($this->paths as $path) {
      $path = Filesystem::resolvePath($path);

      $name = basename($path);
      $this->writeStatus(pht("Uploading '%s'...", $name));

      $hash = @sha1_file($path);
      if (!$hash) {
        throw new Exception(pht('Unable to read file "%s"!', $path));
      }
      $length = filesize($path);

      $do_chunk_upload = false;

      $phid = null;
      try {
        $result = $conduit->callMethodSynchronous(
          'file.allocate',
          array(
            'name' => $name,
            'contentLength' => $length,
            'contentHash' => $hash,
          ));

        $phid = $result['filePHID'];
        if (!$result['upload']) {
          if (!$phid) {
            $this->writeStatus(
              pht(
                'Unable to upload file "%s": the server refused to accept '.
                'it. This usually means it is too large.',
                $name));
            continue;
          }
          // Otherwise, the server completed the upload by referencing known
          // file data.
        } else {
          if ($phid) {
            $do_chunk_upload = true;
          } else {
            // This is a small file that doesn't need to be uploaded in
            // chunks, so continue normally.
          }
        }
      } catch (Exception $ex) {
        $this->writeStatus(
          pht('Unable to use allocate method, trying older upload method.'));
      }

      if ($do_chunk_upload) {
        $this->uploadChunks($phid, $path);
      }

      if (!$phid) {
        try {
          $data = Filesystem::readFile($path);
        } catch (FilesystemException $ex) {
          $this->writeStatus(
            pht('Unable to read file "%s".', $ex->getMessage()));
          $results[$path] = null;
          continue;
        }

        $phid = $conduit->callMethodSynchronous(
          'file.upload',
          array(
{'fraction_non_alphanumeric': 0.1394167758846658, 'fraction_numerical': 0.000327653997378768, 'mean_word_length': 1.9280575539568345, 'pattern_counts': {'":': 2, '<': 17, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4319007', 'n_tokens_mistral': 1804, 'n_tokens_neox': 1762, 'n_words': 457}