AdvancedValueBinder.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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. * @throws \PhpOffice\PhpSpreadsheet\Exception
  17. *
  18. * @return bool
  19. */
  20. public function bindValue(Cell $cell, $value = null)
  21. {
  22. // sanitize UTF-8 strings
  23. if (is_string($value)) {
  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 number in scientific format
  39. if (preg_match('/^' . Calculation::CALCULATION_REGEXP_NUMBER . '$/', $value)) {
  40. $cell->setValueExplicit((float) $value, DataType::TYPE_NUMERIC);
  41. return true;
  42. }
  43. // Check for fraction
  44. if (preg_match('/^([+-]?)\s*(\d+)\s?\/\s*(\d+)$/', $value, $matches)) {
  45. // Convert value to number
  46. $value = $matches[2] / $matches[3];
  47. if ($matches[1] == '-') {
  48. $value = 0 - $value;
  49. }
  50. $cell->setValueExplicit((float) $value, DataType::TYPE_NUMERIC);
  51. // Set style
  52. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  53. ->getNumberFormat()->setFormatCode('??/??');
  54. return true;
  55. } elseif (preg_match('/^([+-]?)(\d*) +(\d*)\s?\/\s*(\d*)$/', $value, $matches)) {
  56. // Convert value to number
  57. $value = $matches[2] + ($matches[3] / $matches[4]);
  58. if ($matches[1] == '-') {
  59. $value = 0 - $value;
  60. }
  61. $cell->setValueExplicit((float) $value, DataType::TYPE_NUMERIC);
  62. // Set style
  63. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  64. ->getNumberFormat()->setFormatCode('# ??/??');
  65. return true;
  66. }
  67. // Check for percentage
  68. if (preg_match('/^\-?\d*\.?\d*\s?\%$/', $value)) {
  69. // Convert value to number
  70. $value = (float) str_replace('%', '', $value) / 100;
  71. $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
  72. // Set style
  73. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  74. ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_PERCENTAGE_00);
  75. return true;
  76. }
  77. // Check for currency
  78. $currencyCode = StringHelper::getCurrencyCode();
  79. $decimalSeparator = StringHelper::getDecimalSeparator();
  80. $thousandsSeparator = StringHelper::getThousandsSeparator();
  81. if (preg_match('/^' . preg_quote($currencyCode, '/') . ' *(\d{1,3}(' . preg_quote($thousandsSeparator, '/') . '\d{3})*|(\d+))(' . preg_quote($decimalSeparator, '/') . '\d{2})?$/', $value)) {
  82. // Convert value to number
  83. $value = (float) trim(str_replace([$currencyCode, $thousandsSeparator, $decimalSeparator], ['', '', '.'], $value));
  84. $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
  85. // Set style
  86. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  87. ->getNumberFormat()->setFormatCode(
  88. str_replace('$', $currencyCode, NumberFormat::FORMAT_CURRENCY_USD_SIMPLE)
  89. );
  90. return true;
  91. } elseif (preg_match('/^\$ *(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?$/', $value)) {
  92. // Convert value to number
  93. $value = (float) trim(str_replace(['$', ','], '', $value));
  94. $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
  95. // Set style
  96. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  97. ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_CURRENCY_USD_SIMPLE);
  98. return true;
  99. }
  100. // Check for time without seconds e.g. '9:45', '09:45'
  101. if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d$/', $value)) {
  102. // Convert value to number
  103. list($h, $m) = explode(':', $value);
  104. $days = $h / 24 + $m / 1440;
  105. $cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
  106. // Set style
  107. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  108. ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_TIME3);
  109. return true;
  110. }
  111. // Check for time with seconds '9:45:59', '09:45:59'
  112. if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$/', $value)) {
  113. // Convert value to number
  114. list($h, $m, $s) = explode(':', $value);
  115. $days = $h / 24 + $m / 1440 + $s / 86400;
  116. // Convert value to number
  117. $cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
  118. // Set style
  119. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  120. ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_TIME4);
  121. return true;
  122. }
  123. // Check for datetime, e.g. '2008-12-31', '2008-12-31 15:59', '2008-12-31 15:59:10'
  124. if (($d = Date::stringToExcel($value)) !== false) {
  125. // Convert value to number
  126. $cell->setValueExplicit($d, DataType::TYPE_NUMERIC);
  127. // Determine style. Either there is a time part or not. Look for ':'
  128. if (strpos($value, ':') !== false) {
  129. $formatCode = 'yyyy-mm-dd h:mm';
  130. } else {
  131. $formatCode = 'yyyy-mm-dd';
  132. }
  133. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  134. ->getNumberFormat()->setFormatCode($formatCode);
  135. return true;
  136. }
  137. // Check for newline character "\n"
  138. if (strpos($value, "\n") !== false) {
  139. $value = StringHelper::sanitizeUTF8($value);
  140. $cell->setValueExplicit($value, DataType::TYPE_STRING);
  141. // Set style
  142. $cell->getWorksheet()->getStyle($cell->getCoordinate())
  143. ->getAlignment()->setWrapText(true);
  144. return true;
  145. }
  146. }
  147. // Not bound yet? Use parent...
  148. return parent::bindValue($cell, $value);
  149. }
  150. }