Skip to content

Commit

Permalink
Resolve merge conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
frantzmiccoli committed Mar 11, 2019
2 parents b1ef7ef + 8b589c4 commit aa38599
Show file tree
Hide file tree
Showing 23 changed files with 861 additions and 65 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com)
and this project adheres to [Semantic Versioning](https://semver.org).

## [Unreleased] -

### Added

- Added support for inline styles in Html reader (borders, alignment, width, height)
- QuotedText cells no longer treated as formulae if the content begins with a `=`
- Clean handling for DDE in formulae

### Fixed

- Fix handling for escaped enclosures and new lines in CSV Separator Inference
- Fix MATCH an error was appearing when comparing strings against 0 (always true)

## [1.6.0] - 2019-01-02

### Added
Expand Down
82 changes: 82 additions & 0 deletions docs/topics/accessing-cells.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,61 @@ $spreadsheet->getActiveSheet()
->setValue('Some value');
```

### Creating a new Cell

If you make a call to `getCell()`, and the cell doesn't already exist, then
PhpSpreadsheet will (by default) create the cell for you. If you don't want
to create a new cell, then you can pass a second argument of false, and then
`getCell()` will return a null if the cell doesn't exist.

### BEWARE: Cells assigned to variables as a Detached Reference

As an "in-memory" model, PHPSpreadsheet can be very demanding of memory,
particularly when working with large spreadsheets. One technique used to
reduce this memory overhead is cell caching, so cells are actually
maintained in a collection that may or may not be held in memory while you
are working with the spreadsheet. Because of this, a call to `getCell()`
(or any similar method) returns the cell data, and a pointer to the collection.
While this is not normally an issue, it can become significant
if you assign the result of a call to `getCell()` to a variable. Any
subsequent calls to retrieve other cells will unset that pointer, although
the cell object will still retain its data values.

What does this mean? Consider the following code:

```
$spreadSheet = new Spreadsheet();
$workSheet = $spreadSheet->getActiveSheet();
// Set details for the formula that we want to evaluate, together with any data on which it depends
$workSheet->fromArray(
[1, 2, 3],
null,
'A1'
);
$cellC1 = $workSheet->getCell('C1');
echo 'Value: ', $cellC1->getValue(), '; Address: ', $cellC1->getCoordinate(), PHP_EOL;
$cellA1 = $workSheet->getCell('A1');
echo 'Value: ', $cellA1->getValue(), '; Address: ', $cellA1->getCoordinate(), PHP_EOL;
echo 'Value: ', $cellC1->getValue(), '; Address: ', $cellC1->getCoordinate(), PHP_EOL;
```

The call to `getCell('C1')` returns the cell at `C1` containing its value (`3`),
together with its link to the collection (used to identify its
address/coordinate `C1`). The subsequent call to access cell `A1`
modifies the value of `$cellC1`, detaching its link to the collection.

So when we try to display the value and address a second time, we can display
its value, but trying to display its address/coordinate will throw an
exception because that link has been set to null.

__Note:__ There are some internal methods that will fetch other cells from the
collection, and this too will detach the link to the collection from any cell
that you might have assigned to a variable.

## Excel DataTypes

MS Excel supports 7 basic datatypes:
Expand Down Expand Up @@ -86,6 +141,33 @@ Formats handled by the advanced value binder include:
You can read more about value binders later in this section of the
documentation.

### Setting a formula in a Cell

As stated above, if you store a string value with the first character an `=`
in a cell. PHPSpreadsheet will treat that value as a formula, and then you
can evaluate that formula by calling `getCalculatedValue()` against the cell.

There may be times though, when you wish to store a value beginning with `=`
as a string, and that you don't want PHPSpreadsheet to evaluate as though it
was a formula.

To do this, you need to "escape" the value by setting it as "quoted text".

```
// Set cell A4 with a formula
$spreadsheet->getActiveSheet()->setCellValue(
'A4',
'=IF(A3, CONCATENATE(A1, " ", A2), CONCATENATE(A2, " ", A1))'
);
$spreadsheet->getActiveSheet()->getCell('A4')
->->getStyle()->setQuotePrefix(true);
```

Then, even if you ask PHPSpreadsheet to return the calculated value for cell
`A4`, it will return `=IF(A3, CONCATENATE(A1, " ", A2), CONCATENATE(A2, " ", A1))`
as a string, and not try to evaluate the formula.


### Setting a date and/or time value in a cell

Date or time values are held as timestamp in Excel (a simple floating
Expand Down
24 changes: 24 additions & 0 deletions docs/topics/calculation-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,30 @@ inserted 2 new rows), changed to "SUM(E4:E11)". Also, the inserted cells
duplicate style information of the previous cell, just like Excel's
behaviour. Note that you can both insert rows and columns.

## Calculation Cache

Once the Calculation engine has evaluated the formula in a cell, the result
will be cached, so if you call `getCalculatedValue()` a second time for the
same cell, the result will be returned from the cache rather than evaluating
the formula a second time. This helps boost performance, because evaluating
a formula is an expensive operation in terms of performance and speed.

However, there may be times when you don't want this, perhaps you've changed
the underlying data and need to re-evaluate the same formula with that new
data.

```
Calculation::getInstance($spreadsheet)->disableCalculationCache();
```

Will disable calculation caching, and flush the current calculation cache.

If you want only to flush the cache, then you can call

```
Calculation::getInstance($spreadsheet)->clearCalculationCache();
```

## Known limitations

There are some known limitations to the PhpSpreadsheet calculation
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder;
use PhpOffice\PhpSpreadsheet\IOFactory;

require __DIR__ . '/../Header.php';

Cell::setValueBinder(new StringValueBinder());

$inputFileType = 'Csv';
$inputFileName = __DIR__ . '/sampleData/longIntegers.csv';

$reader = IOFactory::createReader($inputFileType);
$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' into WorkSheet #1 using IOFactory with a defined reader type of ' . $inputFileType);

$spreadsheet = $reader->load($inputFileName);
$spreadsheet->getActiveSheet()->setTitle(pathinfo($inputFileName, PATHINFO_BASENAME));

$helper->log($spreadsheet->getSheetCount() . ' worksheet' . (($spreadsheet->getSheetCount() == 1) ? '' : 's') . ' loaded');
$loadedSheetNames = $spreadsheet->getSheetNames();
foreach ($loadedSheetNames as $sheetIndex => $loadedSheetName) {
$helper->log('<b>Worksheet #' . $sheetIndex . ' -> ' . $loadedSheetName . ' (Formatted)</b>');
$spreadsheet->setActiveSheetIndexByName($loadedSheetName);
$sheetData = $spreadsheet->getActiveSheet()->toArray(null, true, true, true);
var_dump($sheetData);
}
6 changes: 6 additions & 0 deletions samples/Reader/sampleData/longIntegers.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"Column 1","Column 2"
123456789012345678901234,234567890123456789012345
345678901234567890123456,456789012345678901234567
567890123456789012345678,678901234567890123456789
789012345678901234567890,890123456789012345678901
901234567890123456789012,012345678901234567890123
18 changes: 13 additions & 5 deletions src/PhpSpreadsheet/Calculation/Calculation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2216,8 +2216,8 @@ public function __construct(Spreadsheet $spreadsheet = null)
private static function loadLocales()
{
$localeFileDirectory = __DIR__ . '/locale/';
foreach (glob($localeFileDirectory . '/*', GLOB_ONLYDIR) as $filename) {
$filename = substr($filename, strlen($localeFileDirectory) + 1);
foreach (glob($localeFileDirectory . '*', GLOB_ONLYDIR) as $filename) {
$filename = substr($filename, strlen($localeFileDirectory));
if ($filename != 'en') {
self::$validLocaleLanguages[] = $filename;
}
Expand Down Expand Up @@ -2449,7 +2449,6 @@ public function setLocale($locale)
if (strpos($locale, '_') !== false) {
list($language) = explode('_', $locale);
}

if (count(self::$validLocaleLanguages) == 1) {
self::loadLocales();
}
Expand Down Expand Up @@ -2740,7 +2739,7 @@ public function calculate(Cell $pCell = null)
* @param Cell $pCell Cell to calculate
* @param bool $resetLog Flag indicating whether the debug log should be reset or not
*
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return mixed
*/
Expand Down Expand Up @@ -2844,7 +2843,7 @@ public function parseFormula($formula)
* @param string $cellID Address of the cell to calculate
* @param Cell $pCell Cell to calculate
*
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return mixed
*/
Expand Down Expand Up @@ -2929,6 +2928,15 @@ public function _calculateFormulaValue($formula, $cellID = null, Cell $pCell = n
{
$cellValue = null;

// Quote-Prefixed cell values cannot be formulae, but are treated as strings
if ($pCell !== null && $pCell->getStyle()->getQuotePrefix() === true) {
return self::wrapResult((string) $formula);
}

if (preg_match('/^=\s*cmd\s*\|/miu', $formula) !== 0) {
return self::wrapResult($formula);
}

// Basic validation that this is indeed a formula
// We simply return the cell value if not
$formula = trim($formula);
Expand Down
8 changes: 6 additions & 2 deletions src/PhpSpreadsheet/Calculation/LookupRef.php
Original file line number Diff line number Diff line change
Expand Up @@ -522,9 +522,13 @@ public static function MATCH($lookupValue, $lookupArray, $matchType = 1)

if ($matchType == 0 || $matchType == 1) {
foreach ($lookupArray as $i => $lookupArrayValue) {
if (($matchType == 0) && ($lookupArrayValue == $lookupValue)) {
$onlyNumeric = is_numeric($lookupArrayValue) && is_numeric($lookupValue);
$onlyNumericExactMatch = $onlyNumeric && $lookupArrayValue == $lookupValue;
$nonOnlyNumericExactMatch = !$onlyNumeric && $lookupArrayValue === $lookupValue;
$exactMatch = $onlyNumericExactMatch || $nonOnlyNumericExactMatch;
if (($matchType == 0) && $exactMatch) {
// exact match
return ++$i;
return $i + 1;
} elseif (($matchType == 1) && ($lookupArrayValue <= $lookupValue)) {
$i = array_search($i, $keySet);

Expand Down
2 changes: 2 additions & 0 deletions src/PhpSpreadsheet/Cell/AdvancedValueBinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
* @param Cell $cell Cell to bind value to
* @param mixed $value Value to bind in cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return bool
*/
public function bindValue(Cell $cell, $value = null)
Expand Down
2 changes: 2 additions & 0 deletions src/PhpSpreadsheet/Cell/DefaultValueBinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class DefaultValueBinder implements IValueBinder
* @param Cell $cell Cell to bind value to
* @param mixed $value Value to bind in cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return bool
*/
public function bindValue(Cell $cell, $value)
Expand Down
31 changes: 31 additions & 0 deletions src/PhpSpreadsheet/Cell/StringValueBinder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace PhpOffice\PhpSpreadsheet\Cell;

use PhpOffice\PhpSpreadsheet\Shared\StringHelper;

class StringValueBinder implements IValueBinder
{
/**
* Bind value to a cell.
*
* @param Cell $cell Cell to bind value to
* @param mixed $value Value to bind in cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return bool
*/
public function bindValue(Cell $cell, $value)
{
// sanitize UTF-8 strings
if (is_string($value)) {
$value = StringHelper::sanitizeUTF8($value);
}

$cell->setValueExplicit((string) $value, DataType::TYPE_STRING);

// Done!
return true;
}
}
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Collection/Cells.php
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ public function getHighestColumn($row = null)
$columnList[] = Coordinate::columnIndexFromString($c);
}

return Coordinate::stringFromColumnIndex(max($columnList) + 1);
return Coordinate::stringFromColumnIndex(max($columnList));
}

/**
Expand Down
14 changes: 6 additions & 8 deletions src/PhpSpreadsheet/Reader/Csv.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ protected function inferSeparator()
return;
}

$potentialDelimiters = [',', ';', "\t", '|', ':', ' '];
$potentialDelimiters = [',', ';', "\t", '|', ':', ' ', '~'];
$counts = [];
foreach ($potentialDelimiters as $delimiter) {
$counts[$delimiter] = [];
Expand Down Expand Up @@ -254,15 +254,13 @@ private function getNextLine($line = '')
$line = $line . $newLine;

// Drop everything that is enclosed to avoid counting false positives in enclosures
$enclosure = preg_quote($this->enclosure, '/');
$line = preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/U', '', $line);
$enclosure = '(?<!' . preg_quote($this->escapeCharacter, '/') . ')'
. preg_quote($this->enclosure, '/');
$line = preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/Us', '', $line);

// See if we have any enclosures left in the line
$matches = [];
preg_match('/(' . $enclosure . ')/', $line, $matches);

// if we still have an enclosure then we need to read the next line aswell
if (count($matches) > 0) {
// if we still have an enclosure then we need to read the next line as well
if (preg_match('/(' . $enclosure . ')/', $line) > 0) {
$line = $this->getNextLine($line);
}

Expand Down
Loading

0 comments on commit aa38599

Please sign in to comment.