/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php

https://github.com/sbourget/moodle · PHP · 205 lines · 132 code · 34 blank · 39 comment · 17 complexity · 03363113dc7925e406767f8c1ce79191 MD5 · raw file

  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Cell;
  3. use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
  4. use PhpOffice\PhpSpreadsheet\RichText\RichText;
  5. use PhpOffice\PhpSpreadsheet\Shared\Date;
  6. use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
  7. use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
  8. class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
  9. {
  10. /**
  11. * Bind value to a cell.
  12. *
  13. * @param Cell $cell Cell to bind value to
  14. * @param mixed $value Value to bind in cell
  15. *
  16. * @return bool
  17. */
  18. public function bindValue(Cell $cell, $value = null)
  19. {
  20. if ($value === null) {
  21. return parent::bindValue($cell, $value);
  22. } elseif (is_string($value)) {
  23. // sanitize UTF-8 strings
  24. $value = StringHelper::sanitizeUTF8($value);
  25. }
  26. // Find out data type
  27. $dataType = parent::dataTypeForValue($value);
  28. // Style logic - strings
  29. if ($dataType === DataType::TYPE_STRING && !$value instanceof RichText) {
  30. // Test for booleans using locale-setting
  31. if ($value == Calculation::getTRUE()) {
  32. $cell->setValueExplicit(true, DataType::TYPE_BOOL);
  33. return true;
  34. } elseif ($value == Calculation::getFALSE()) {
  35. $cell->setValueExplicit(false, DataType::TYPE_BOOL);
  36. return true;
  37. }
  38. // Check for fractions
  39. if (preg_match('/^([+-]?)\s*(\d+)\s?\/\s*(\d+)$/', $value, $matches)) {
  40. return $this->setProperFraction($matches, $cell);
  41. } elseif (preg_match('/^([+-]?)(\d*) +(\d*)\s?\/\s*(\d*)$/', $value, $matches)) {
  42. return $this->setImproperFraction($matches, $cell);
  43. }
  44. // Check for percentage
  45. if (preg_match('/^\-?\d*\.?\d*\s?\%$/', $value)) {
  46. return $this->setPercentage($value, $cell);
  47. }
  48. // Check for currency
  49. $currencyCode = StringHelper::getCurrencyCode();
  50. $decimalSeparator = StringHelper::getDecimalSeparator();
  51. $thousandsSeparator = StringHelper::getThousandsSeparator();
  52. if (preg_match('/^' . preg_quote($currencyCode, '/') . ' *(\d{1,3}(' . preg_quote($thousandsSeparator, '/') . '\d{3})*|(\d+))(' . preg_quote($decimalSeparator, '/') . '\d{2})?$/', $value)) {
  53. // Convert value to number
  54. $value = (float) trim(str_replace([$currencyCode, $thousandsSeparator, $decimalSeparator], ['', '', '.'], $value));
  55. $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
  56. // Set style
  57. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  58. ->getNumberFormat()->setFormatCode(
  59. str_replace('$', $currencyCode, NumberFormat::FORMAT_CURRENCY_USD_SIMPLE)
  60. );
  61. return true;
  62. } elseif (preg_match('/^\$ *(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?$/', $value)) {
  63. // Convert value to number
  64. $value = (float) trim(str_replace(['$', ','], '', $value));
  65. $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
  66. // Set style
  67. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  68. ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_CURRENCY_USD_SIMPLE);
  69. return true;
  70. }
  71. // Check for time without seconds e.g. '9:45', '09:45'
  72. if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d$/', $value)) {
  73. return $this->setTimeHoursMinutes($value, $cell);
  74. }
  75. // Check for time with seconds '9:45:59', '09:45:59'
  76. if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$/', $value)) {
  77. return $this->setTimeHoursMinutesSeconds($value, $cell);
  78. }
  79. // Check for datetime, e.g. '2008-12-31', '2008-12-31 15:59', '2008-12-31 15:59:10'
  80. if (($d = Date::stringToExcel($value)) !== false) {
  81. // Convert value to number
  82. $cell->setValueExplicit($d, DataType::TYPE_NUMERIC);
  83. // Determine style. Either there is a time part or not. Look for ':'
  84. if (strpos($value, ':') !== false) {
  85. $formatCode = 'yyyy-mm-dd h:mm';
  86. } else {
  87. $formatCode = 'yyyy-mm-dd';
  88. }
  89. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  90. ->getNumberFormat()->setFormatCode($formatCode);
  91. return true;
  92. }
  93. // Check for newline character "\n"
  94. if (strpos($value, "\n") !== false) {
  95. $cell->setValueExplicit($value, DataType::TYPE_STRING);
  96. // Set style
  97. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  98. ->getAlignment()->setWrapText(true);
  99. return true;
  100. }
  101. }
  102. // Not bound yet? Use parent...
  103. return parent::bindValue($cell, $value);
  104. }
  105. protected function setImproperFraction(array $matches, Cell $cell): bool
  106. {
  107. // Convert value to number
  108. $value = $matches[2] + ($matches[3] / $matches[4]);
  109. if ($matches[1] === '-') {
  110. $value = 0 - $value;
  111. }
  112. $cell->setValueExplicit((float) $value, DataType::TYPE_NUMERIC);
  113. // Build the number format mask based on the size of the matched values
  114. $dividend = str_repeat('?', strlen($matches[3]));
  115. $divisor = str_repeat('?', strlen($matches[4]));
  116. $fractionMask = "# {$dividend}/{$divisor}";
  117. // Set style
  118. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  119. ->getNumberFormat()->setFormatCode($fractionMask);
  120. return true;
  121. }
  122. protected function setProperFraction(array $matches, Cell $cell): bool
  123. {
  124. // Convert value to number
  125. $value = $matches[2] / $matches[3];
  126. if ($matches[1] === '-') {
  127. $value = 0 - $value;
  128. }
  129. $cell->setValueExplicit((float) $value, DataType::TYPE_NUMERIC);
  130. // Build the number format mask based on the size of the matched values
  131. $dividend = str_repeat('?', strlen($matches[2]));
  132. $divisor = str_repeat('?', strlen($matches[3]));
  133. $fractionMask = "{$dividend}/{$divisor}";
  134. // Set style
  135. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  136. ->getNumberFormat()->setFormatCode($fractionMask);
  137. return true;
  138. }
  139. protected function setPercentage(string $value, Cell $cell): bool
  140. {
  141. // Convert value to number
  142. $value = ((float) str_replace('%', '', $value)) / 100;
  143. $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
  144. // Set style
  145. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  146. ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_PERCENTAGE_00);
  147. return true;
  148. }
  149. protected function setTimeHoursMinutes(string $value, Cell $cell): bool
  150. {
  151. // Convert value to number
  152. [$hours, $minutes] = explode(':', $value);
  153. $days = ($hours / 24) + ($minutes / 1440);
  154. $cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
  155. // Set style
  156. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  157. ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_TIME3);
  158. return true;
  159. }
  160. protected function setTimeHoursMinutesSeconds(string $value, Cell $cell): bool
  161. {
  162. // Convert value to number
  163. [$hours, $minutes, $seconds] = explode(':', $value);
  164. $days = ($hours / 24) + ($minutes / 1440) + ($seconds / 86400);
  165. $cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
  166. // Set style
  167. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  168. ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_TIME4);
  169. return true;
  170. }
  171. }