| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698 | <?phpnamespace PhpOffice\PhpSpreadsheet\Cell;use PhpOffice\PhpSpreadsheet\Calculation\Calculation;use PhpOffice\PhpSpreadsheet\Collection\Cells;use PhpOffice\PhpSpreadsheet\Exception;use PhpOffice\PhpSpreadsheet\RichText\RichText;use PhpOffice\PhpSpreadsheet\Style\NumberFormat;use PhpOffice\PhpSpreadsheet\Style\Style;use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;class Cell{    /**     * Value binder to use.     *     * @var IValueBinder     */    private static $valueBinder;    /**     * Value of the cell.     *     * @var mixed     */    private $value;    /**     *    Calculated value of the cell (used for caching)     *    This returns the value last calculated by MS Excel or whichever spreadsheet program was used to     *        create the original spreadsheet file.     *    Note that this value is not guaranteed to reflect the actual calculated value because it is     *        possible that auto-calculation was disabled in the original spreadsheet, and underlying data     *        values used by the formula have changed since it was last calculated.     *     * @var mixed     */    private $calculatedValue;    /**     * Type of the cell data.     *     * @var string     */    private $dataType;    /**     * Collection of cells.     *     * @var Cells     */    private $parent;    /**     * Index to cellXf.     *     * @var int     */    private $xfIndex = 0;    /**     * Attributes of the formula.     */    private $formulaAttributes;    /**     * Update the cell into the cell collection.     *     * @return self     */    public function updateInCollection()    {        $this->parent->update($this);        return $this;    }    public function detach()    {        $this->parent = null;    }    public function attach(Cells $parent)    {        $this->parent = $parent;    }    /**     * Create a new Cell.     *     * @param mixed $pValue     * @param string $pDataType     * @param Worksheet $pSheet     *     * @throws Exception     */    public function __construct($pValue, $pDataType, Worksheet $pSheet)    {        // Initialise cell value        $this->value = $pValue;        // Set worksheet cache        $this->parent = $pSheet->getCellCollection();        // Set datatype?        if ($pDataType !== null) {            if ($pDataType == DataType::TYPE_STRING2) {                $pDataType = DataType::TYPE_STRING;            }            $this->dataType = $pDataType;        } elseif (!self::getValueBinder()->bindValue($this, $pValue)) {            throw new Exception('Value could not be bound to cell.');        }    }    /**     * Get cell coordinate column.     *     * @return string     */    public function getColumn()    {        return $this->parent->getCurrentColumn();    }    /**     * Get cell coordinate row.     *     * @return int     */    public function getRow()    {        return $this->parent->getCurrentRow();    }    /**     * Get cell coordinate.     *     * @return string     */    public function getCoordinate()    {        return $this->parent->getCurrentCoordinate();    }    /**     * Get cell value.     *     * @return mixed     */    public function getValue()    {        return $this->value;    }    /**     * Get cell value with formatting.     *     * @return string     */    public function getFormattedValue()    {        return (string) NumberFormat::toFormattedString(            $this->getCalculatedValue(),            $this->getStyle()                ->getNumberFormat()->getFormatCode()        );    }    /**     * Set cell value.     *     *    Sets the value for a cell, automatically determining the datatype using the value binder     *     * @param mixed $pValue Value     *     * @throws Exception     *     * @return Cell     */    public function setValue($pValue)    {        if (!self::getValueBinder()->bindValue($this, $pValue)) {            throw new Exception('Value could not be bound to cell.');        }        return $this;    }    /**     * Set the value for a cell, with the explicit data type passed to the method (bypassing any use of the value binder).     *     * @param mixed $pValue Value     * @param string $pDataType Explicit data type, see DataType::TYPE_*     *     * @throws Exception     *     * @return Cell     */    public function setValueExplicit($pValue, $pDataType)    {        // set the value according to data type        switch ($pDataType) {            case DataType::TYPE_NULL:                $this->value = $pValue;                break;            case DataType::TYPE_STRING2:                $pDataType = DataType::TYPE_STRING;                // no break            case DataType::TYPE_STRING:                // Synonym for string            case DataType::TYPE_INLINE:                // Rich text                $this->value = DataType::checkString($pValue);                break;            case DataType::TYPE_NUMERIC:                $this->value = (float) $pValue;                break;            case DataType::TYPE_FORMULA:                $this->value = (string) $pValue;                break;            case DataType::TYPE_BOOL:                $this->value = (bool) $pValue;                break;            case DataType::TYPE_ERROR:                $this->value = DataType::checkErrorCode($pValue);                break;            default:                throw new Exception('Invalid datatype: ' . $pDataType);                break;        }        // set the datatype        $this->dataType = $pDataType;        return $this->updateInCollection();    }    /**     * Get calculated cell value.     *     * @param bool $resetLog Whether the calculation engine logger should be reset or not     *     * @throws Exception     *     * @return mixed     */    public function getCalculatedValue($resetLog = true)    {        if ($this->dataType == DataType::TYPE_FORMULA) {            try {                $result = Calculation::getInstance(                    $this->getWorksheet()->getParent()                )->calculateCellValue($this, $resetLog);                //    We don't yet handle array returns                if (is_array($result)) {                    while (is_array($result)) {                        $result = array_pop($result);                    }                }            } catch (Exception $ex) {                if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->calculatedValue !== null)) {                    return $this->calculatedValue; // Fallback for calculations referencing external files.                }                throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception(                    $this->getWorksheet()->getTitle() . '!' . $this->getCoordinate() . ' -> ' . $ex->getMessage()                );            }            if ($result === '#Not Yet Implemented') {                return $this->calculatedValue; // Fallback if calculation engine does not support the formula.            }            return $result;        } elseif ($this->value instanceof RichText) {            return $this->value->getPlainText();        }        return $this->value;    }    /**     * Set old calculated value (cached).     *     * @param mixed $pValue Value     *     * @return Cell     */    public function setCalculatedValue($pValue)    {        if ($pValue !== null) {            $this->calculatedValue = (is_numeric($pValue)) ? (float) $pValue : $pValue;        }        return $this->updateInCollection();    }    /**     *    Get old calculated value (cached)     *    This returns the value last calculated by MS Excel or whichever spreadsheet program was used to     *        create the original spreadsheet file.     *    Note that this value is not guaranteed to reflect the actual calculated value because it is     *        possible that auto-calculation was disabled in the original spreadsheet, and underlying data     *        values used by the formula have changed since it was last calculated.     *     * @return mixed     */    public function getOldCalculatedValue()    {        return $this->calculatedValue;    }    /**     * Get cell data type.     *     * @return string     */    public function getDataType()    {        return $this->dataType;    }    /**     * Set cell data type.     *     * @param string $pDataType see DataType::TYPE_*     *     * @return Cell     */    public function setDataType($pDataType)    {        if ($pDataType == DataType::TYPE_STRING2) {            $pDataType = DataType::TYPE_STRING;        }        $this->dataType = $pDataType;        return $this->updateInCollection();    }    /**     * Identify if the cell contains a formula.     *     * @return bool     */    public function isFormula()    {        return $this->dataType == DataType::TYPE_FORMULA;    }    /**     *    Does this cell contain Data validation rules?     *     * @throws Exception     *     * @return bool     */    public function hasDataValidation()    {        if (!isset($this->parent)) {            throw new Exception('Cannot check for data validation when cell is not bound to a worksheet');        }        return $this->getWorksheet()->dataValidationExists($this->getCoordinate());    }    /**     * Get Data validation rules.     *     * @throws Exception     *     * @return DataValidation     */    public function getDataValidation()    {        if (!isset($this->parent)) {            throw new Exception('Cannot get data validation for cell that is not bound to a worksheet');        }        return $this->getWorksheet()->getDataValidation($this->getCoordinate());    }    /**     * Set Data validation rules.     *     * @param DataValidation $pDataValidation     *     * @throws Exception     *     * @return Cell     */    public function setDataValidation(DataValidation $pDataValidation = null)    {        if (!isset($this->parent)) {            throw new Exception('Cannot set data validation for cell that is not bound to a worksheet');        }        $this->getWorksheet()->setDataValidation($this->getCoordinate(), $pDataValidation);        return $this->updateInCollection();    }    /**     * Does this cell contain valid value?     *     * @return bool     */    public function hasValidValue()    {        $validator = new DataValidator();        return $validator->isValid($this);    }    /**     * Does this cell contain a Hyperlink?     *     * @throws Exception     *     * @return bool     */    public function hasHyperlink()    {        if (!isset($this->parent)) {            throw new Exception('Cannot check for hyperlink when cell is not bound to a worksheet');        }        return $this->getWorksheet()->hyperlinkExists($this->getCoordinate());    }    /**     * Get Hyperlink.     *     * @throws Exception     *     * @return Hyperlink     */    public function getHyperlink()    {        if (!isset($this->parent)) {            throw new Exception('Cannot get hyperlink for cell that is not bound to a worksheet');        }        return $this->getWorksheet()->getHyperlink($this->getCoordinate());    }    /**     * Set Hyperlink.     *     * @param Hyperlink $pHyperlink     *     * @throws Exception     *     * @return Cell     */    public function setHyperlink(Hyperlink $pHyperlink = null)    {        if (!isset($this->parent)) {            throw new Exception('Cannot set hyperlink for cell that is not bound to a worksheet');        }        $this->getWorksheet()->setHyperlink($this->getCoordinate(), $pHyperlink);        return $this->updateInCollection();    }    /**     * Get cell collection.     *     * @return Cells     */    public function getParent()    {        return $this->parent;    }    /**     * Get parent worksheet.     *     * @return Worksheet     */    public function getWorksheet()    {        return $this->parent->getParent();    }    /**     * Is this cell in a merge range.     *     * @return bool     */    public function isInMergeRange()    {        return (bool) $this->getMergeRange();    }    /**     * Is this cell the master (top left cell) in a merge range (that holds the actual data value).     *     * @return bool     */    public function isMergeRangeValueCell()    {        if ($mergeRange = $this->getMergeRange()) {            $mergeRange = Coordinate::splitRange($mergeRange);            list($startCell) = $mergeRange[0];            if ($this->getCoordinate() === $startCell) {                return true;            }        }        return false;    }    /**     * If this cell is in a merge range, then return the range.     *     * @return string     */    public function getMergeRange()    {        foreach ($this->getWorksheet()->getMergeCells() as $mergeRange) {            if ($this->isInRange($mergeRange)) {                return $mergeRange;            }        }        return false;    }    /**     * Get cell style.     *     * @return Style     */    public function getStyle()    {        return $this->getWorksheet()->getStyle($this->getCoordinate());    }    /**     * Re-bind parent.     *     * @param Worksheet $parent     *     * @return Cell     */    public function rebindParent(Worksheet $parent)    {        $this->parent = $parent->getCellCollection();        return $this->updateInCollection();    }    /**     *    Is cell in a specific range?     *     * @param string $pRange Cell range (e.g. A1:A1)     *     * @return bool     */    public function isInRange($pRange)    {        list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($pRange);        // Translate properties        $myColumn = Coordinate::columnIndexFromString($this->getColumn());        $myRow = $this->getRow();        // Verify if cell is in range        return ($rangeStart[0] <= $myColumn) && ($rangeEnd[0] >= $myColumn) &&                ($rangeStart[1] <= $myRow) && ($rangeEnd[1] >= $myRow);    }    /**     * Compare 2 cells.     *     * @param Cell $a Cell a     * @param Cell $b Cell b     *     * @return int Result of comparison (always -1 or 1, never zero!)     */    public static function compareCells(self $a, self $b)    {        if ($a->getRow() < $b->getRow()) {            return -1;        } elseif ($a->getRow() > $b->getRow()) {            return 1;        } elseif (Coordinate::columnIndexFromString($a->getColumn()) < Coordinate::columnIndexFromString($b->getColumn())) {            return -1;        }        return 1;    }    /**     * Get value binder to use.     *     * @return IValueBinder     */    public static function getValueBinder()    {        if (self::$valueBinder === null) {            self::$valueBinder = new DefaultValueBinder();        }        return self::$valueBinder;    }    /**     * Set value binder to use.     *     * @param IValueBinder $binder     */    public static function setValueBinder(IValueBinder $binder)    {        self::$valueBinder = $binder;    }    /**     * Implement PHP __clone to create a deep clone, not just a shallow copy.     */    public function __clone()    {        $vars = get_object_vars($this);        foreach ($vars as $key => $value) {            if ((is_object($value)) && ($key != 'parent')) {                $this->$key = clone $value;            } else {                $this->$key = $value;            }        }    }    /**     * Get index to cellXf.     *     * @return int     */    public function getXfIndex()    {        return $this->xfIndex;    }    /**     * Set index to cellXf.     *     * @param int $pValue     *     * @return Cell     */    public function setXfIndex($pValue)    {        $this->xfIndex = $pValue;        return $this->updateInCollection();    }    /**     * Set the formula attributes.     *     * @param mixed $pAttributes     *     * @return Cell     */    public function setFormulaAttributes($pAttributes)    {        $this->formulaAttributes = $pAttributes;        return $this;    }    /**     * Get the formula attributes.     */    public function getFormulaAttributes()    {        return $this->formulaAttributes;    }    /**     * Convert to string.     *     * @return string     */    public function __toString()    {        return (string) $this->getValue();    }}
 |