Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Helper;
   4  
   5  use PhpOffice\PhpSpreadsheet\IOFactory;
   6  use PhpOffice\PhpSpreadsheet\Spreadsheet;
   7  use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
   8  use PhpOffice\PhpSpreadsheet\Writer\IWriter;
   9  use RecursiveDirectoryIterator;
  10  use RecursiveIteratorIterator;
  11  use RecursiveRegexIterator;
  12  use ReflectionClass;
  13  use RegexIterator;
  14  use RuntimeException;
  15  
  16  /**
  17   * Helper class to be used in sample code.
  18   */
  19  class Sample
  20  {
  21      /**
  22       * Returns whether we run on CLI or browser.
  23       *
  24       * @return bool
  25       */
  26      public function isCli()
  27      {
  28          return PHP_SAPI === 'cli';
  29      }
  30  
  31      /**
  32       * Return the filename currently being executed.
  33       *
  34       * @return string
  35       */
  36      public function getScriptFilename()
  37      {
  38          return basename($_SERVER['SCRIPT_FILENAME'], '.php');
  39      }
  40  
  41      /**
  42       * Whether we are executing the index page.
  43       *
  44       * @return bool
  45       */
  46      public function isIndex()
  47      {
  48          return $this->getScriptFilename() === 'index';
  49      }
  50  
  51      /**
  52       * Return the page title.
  53       *
  54       * @return string
  55       */
  56      public function getPageTitle()
  57      {
  58          return $this->isIndex() ? 'PHPSpreadsheet' : $this->getScriptFilename();
  59      }
  60  
  61      /**
  62       * Return the page heading.
  63       *
  64       * @return string
  65       */
  66      public function getPageHeading()
  67      {
  68          return $this->isIndex() ? '' : '<h1>' . str_replace('_', ' ', $this->getScriptFilename()) . '</h1>';
  69      }
  70  
  71      /**
  72       * Returns an array of all known samples.
  73       *
  74       * @return string[][] [$name => $path]
  75       */
  76      public function getSamples()
  77      {
  78          // Populate samples
  79          $baseDir = realpath(__DIR__ . '/../../../samples');
  80          $directory = new RecursiveDirectoryIterator($baseDir);
  81          $iterator = new RecursiveIteratorIterator($directory);
  82          $regex = new RegexIterator($iterator, '/^.+\.php$/', RecursiveRegexIterator::GET_MATCH);
  83  
  84          $files = [];
  85          foreach ($regex as $file) {
  86              $file = str_replace(str_replace('\\', '/', $baseDir) . '/', '', str_replace('\\', '/', $file[0]));
  87              $info = pathinfo($file);
  88              $category = str_replace('_', ' ', $info['dirname'] ?? '');
  89              $name = str_replace('_', ' ', (string) preg_replace('/(|\.php)/', '', $info['filename']));
  90              if (!in_array($category, ['.', 'boostrap', 'templates'])) {
  91                  if (!isset($files[$category])) {
  92                      $files[$category] = [];
  93                  }
  94                  $files[$category][$name] = $file;
  95              }
  96          }
  97  
  98          // Sort everything
  99          ksort($files);
 100          foreach ($files as &$f) {
 101              asort($f);
 102          }
 103  
 104          return $files;
 105      }
 106  
 107      /**
 108       * Write documents.
 109       *
 110       * @param string $filename
 111       * @param string[] $writers
 112       */
 113      public function write(Spreadsheet $spreadsheet, $filename, array $writers = ['Xlsx', 'Xls']): void
 114      {
 115          // Set active sheet index to the first sheet, so Excel opens this as the first sheet
 116          $spreadsheet->setActiveSheetIndex(0);
 117  
 118          // Write documents
 119          foreach ($writers as $writerType) {
 120              $path = $this->getFilename($filename, mb_strtolower($writerType));
 121              $writer = IOFactory::createWriter($spreadsheet, $writerType);
 122              $callStartTime = microtime(true);
 123              $writer->save($path);
 124              $this->logWrite($writer, $path, $callStartTime);
 125          }
 126  
 127          $this->logEndingNotes();
 128      }
 129  
 130      protected function isDirOrMkdir(string $folder): bool
 131      {
 132          return \is_dir($folder) || \mkdir($folder);
 133      }
 134  
 135      /**
 136       * Returns the temporary directory and make sure it exists.
 137       *
 138       * @return string
 139       */
 140      private function getTemporaryFolder()
 141      {
 142          $tempFolder = sys_get_temp_dir() . '/phpspreadsheet';
 143          if (!$this->isDirOrMkdir($tempFolder)) {
 144              throw new RuntimeException(sprintf('Directory "%s" was not created', $tempFolder));
 145          }
 146  
 147          return $tempFolder;
 148      }
 149  
 150      /**
 151       * Returns the filename that should be used for sample output.
 152       *
 153       * @param string $filename
 154       * @param string $extension
 155       *
 156       * @return string
 157       */
 158      public function getFilename($filename, $extension = 'xlsx')
 159      {
 160          $originalExtension = pathinfo($filename, PATHINFO_EXTENSION);
 161  
 162          return $this->getTemporaryFolder() . '/' . str_replace('.' . $originalExtension, '.' . $extension, basename($filename));
 163      }
 164  
 165      /**
 166       * Return a random temporary file name.
 167       *
 168       * @param string $extension
 169       *
 170       * @return string
 171       */
 172      public function getTemporaryFilename($extension = 'xlsx')
 173      {
 174          $temporaryFilename = tempnam($this->getTemporaryFolder(), 'phpspreadsheet-');
 175          unlink($temporaryFilename);
 176  
 177          return $temporaryFilename . '.' . $extension;
 178      }
 179  
 180      public function log($message): void
 181      {
 182          $eol = $this->isCli() ? PHP_EOL : '<br />';
 183          echo date('H:i:s ') . $message . $eol;
 184      }
 185  
 186      public function titles(string $category, string $functionName, ?string $description = null): void
 187      {
 188          $this->log(sprintf('%s Functions:', $category));
 189          $description === null
 190              ? $this->log(sprintf('Function: %s()', rtrim($functionName, '()')))
 191              : $this->log(sprintf('Function: %s() - %s.', rtrim($functionName, '()'), rtrim($description, '.')));
 192      }
 193  
 194      public function displayGrid(array $matrix): void
 195      {
 196          $renderer = new TextGrid($matrix, $this->isCli());
 197          echo $renderer->render();
 198      }
 199  
 200      public function logCalculationResult(
 201          Worksheet $worksheet,
 202          string $functionName,
 203          string $formulaCell,
 204          ?string $descriptionCell = null
 205      ): void {
 206          if ($descriptionCell !== null) {
 207              $this->log($worksheet->getCell($descriptionCell)->getValue());
 208          }
 209          $this->log($worksheet->getCell($formulaCell)->getValue());
 210          $this->log(sprintf('%s() Result is ', $functionName) . $worksheet->getCell($formulaCell)->getCalculatedValue());
 211      }
 212  
 213      /**
 214       * Log ending notes.
 215       */
 216      public function logEndingNotes(): void
 217      {
 218          // Do not show execution time for index
 219          $this->log('Peak memory usage: ' . (memory_get_peak_usage(true) / 1024 / 1024) . 'MB');
 220      }
 221  
 222      /**
 223       * Log a line about the write operation.
 224       *
 225       * @param string $path
 226       * @param float $callStartTime
 227       */
 228      public function logWrite(IWriter $writer, $path, $callStartTime): void
 229      {
 230          $callEndTime = microtime(true);
 231          $callTime = $callEndTime - $callStartTime;
 232          $reflection = new ReflectionClass($writer);
 233          $format = $reflection->getShortName();
 234          $message = "Write {$format} format to <code>{$path}</code>  in " . sprintf('%.4f', $callTime) . ' seconds';
 235  
 236          $this->log($message);
 237      }
 238  
 239      /**
 240       * Log a line about the read operation.
 241       *
 242       * @param string $format
 243       * @param string $path
 244       * @param float $callStartTime
 245       */
 246      public function logRead($format, $path, $callStartTime): void
 247      {
 248          $callEndTime = microtime(true);
 249          $callTime = $callEndTime - $callStartTime;
 250          $message = "Read {$format} format from <code>{$path}</code>  in " . sprintf('%.4f', $callTime) . ' seconds';
 251  
 252          $this->log($message);
 253      }
 254  }