diff --git a/examples/calculations.php b/examples/calculations.php
index e97e580..29bf34d 100644
--- a/examples/calculations.php
+++ b/examples/calculations.php
@@ -4,7 +4,10 @@
use divengine\matrix;
-// Create a matrix
+$F_AMOUNT = fn($r, $c, matrix $m) => $m->{$r + .1} * $m->{$r + .2};
+$F_TOTAL = fn ($r, $c, matrix $m) => array_sum($m->vertical($c, 1, $r - 1));
+$F_CUMUL = fn ($r, $c, matrix $m) => $r == 1 ? $m->get($r, $c - 1)
+ : $m->get($r - 1, $c) + $m->get($r, $c - 1);
$table = new matrix([
["Product", "Price", "Count"],
["Apple", 10, 2],
@@ -12,46 +15,39 @@
["Orange", 6, 10],
]);
-// Show the matrix
-echo $table->format('txt', true);
+echo $table . "\n";
$table->addColumn();
-$table->set(0, 3, "Amount");
+$table->{0.3} = "Amount";
// Fill the column with the product of the previous two columns
-$table->fillVertical(3, 1, 3,
- fn ($r, $c, matrix $m)
- => $m->get($r, $c - 1) * $m->get($r, $c - 2));
+$table->fillVertical(3, 1, 3, $F_AMOUNT);
// Add a row with the sum of the previous rows
-$table->addRow(["Total", "", "",
- fn ($r, $c, matrix $m)
- => array_sum($m->vertical($c, 1, $r - 1))]);
-echo $table->format('txt', true);
+$table->addRow(["Total", "", "", $F_TOTAL]);
+echo $table . "\n";
// Add a column with the sum of the previous columns
$table->addColumn();
-$table->set(0, 4, "Cumul");
+$table->{0.4} = "Cumul";
// Fill the column with the sum of the previous column
-$table->fillVertical(4, 1, 3,
- fn ($r, $c, matrix $m)
- => $r == 1 ? $m->get($r, $c - 1)
- : $m->get($r - 1, $c) + $m->get($r, $c - 1));
-
-echo $table->format('txt', true);
+$table->fillVertical(4, 1, 3, $F_CUMUL);
+echo $table . "\n";
// Change a value of the second row
-$table->set(1, 1, 20);
-echo $table->format('txt', true);
+$table->{1.1} = 20;
+echo $table . "\n";
// Remove the second row
$table->removeRow(1);
-echo $table->format('txt', true);
+echo $table . "\n";
// Show ranges
print_r($table->vertical(1, 1, 2));
print_r($table->horizontal(1, 1, 2));
+print_r($table->range(1, 1, 2, 2));
+echo "\n";
// Serialize
echo $table->format('serialize');
\ No newline at end of file
diff --git a/examples/cells.php b/examples/cells.php
new file mode 100644
index 0000000..6c83387
--- /dev/null
+++ b/examples/cells.php
@@ -0,0 +1,42 @@
+get(1, 1);
+echo $m->{1.1};
+echo "\n";
+
+// echo $m->{0.2};
+echo $m->{.2};
+echo "\n";
+
+// echo $m->{2.2};
+echo $m->{-1.2};
+echo "\n";
+
+for ($i = 0; $i < 3; $i++)
+ for ($j = 0; $j < 3; $j++)
+ echo $m->{$i + ($j / 10)} . " ";
+echo "\n";
+
+for ($i = 0; $i < 3; $i++)
+ for ($j = 0; $j < 3; $j++)
+ echo $m->{($i + ($j / 10) + 1) * -1} . " ";
+echo "\n";
+
+for ($i = 0; $i < 3; $i++)
+ for ($j = 0; $j < 3; $j++)
+ echo $m->{"$i.$j"} . " ";
+
+echo "\n";
+
+$m->{0.0} = 10;
+echo $m;
\ No newline at end of file
diff --git a/examples/create.php b/examples/create.php
new file mode 100644
index 0000000..aa6102c
--- /dev/null
+++ b/examples/create.php
@@ -0,0 +1,28 @@
+formatTXT(false);
+echo PHP_EOL;
+
+// Create a new matrix using the static method create
+$m2 = matrix::create($array);
+echo $m2->formatTXT(false);
+echo PHP_EOL;
+
+// Create an matrix from dimensions
+$m3 = matrix::dimension(5, 5, 0);
+echo $m3->formatTXT(false);
+echo PHP_EOL;
+
+// Create an matrix from a string
+$m4 = matrix::fromJSONFile(__DIR__.'/data/matrix.json');
+echo $m4->formatTXT(false);
+echo PHP_EOL;
diff --git a/examples/custom.php b/examples/custom.php
new file mode 100644
index 0000000..4a14f15
--- /dev/null
+++ b/examples/custom.php
@@ -0,0 +1,34 @@
+ $m->{$r + .1} * $m->{$r +.2};
+$F_TOTAL = fn($r, $c, matrix $m) => array_sum($m->vertical($c, 1, $m->rows - 1));
+$F_AVG = fn($r, $c, matrix $m) => $F_TOTAL($r, $c, $m) / ($m->rows - 1);
+
+class ProductsTable extends matrix
+{
+ public function __construct(array $products)
+ {
+ global $F_AMOUNT, $F_AVG, $F_TOTAL;
+
+ $data = [["Name", "Price", "Qty", "Total"]];
+ foreach ($products as $product)
+ {
+ $data[] = [$product->name, $product->price, $product->quantity, $F_AMOUNT];
+ }
+
+ $data[] = ["Total", $F_AVG, $F_TOTAL, $F_TOTAL];
+
+ parent::__construct($data);
+ }
+}
+
+echo new ProductsTable([
+ (object) ["name" => "Apple", "price" => 1.5, "quantity" => 10],
+ (object) ["name" => "Banana", "price" => 2.5, "quantity" => 5],
+ (object) ["name" => "Orange", "price" => 3.5, "quantity" => 3],
+ (object) ["name" => "Kiwi", "price" => 4.5, "quantity" => 1],
+]);
diff --git a/examples/data/matrix.json b/examples/data/matrix.json
new file mode 100644
index 0000000..106a76f
--- /dev/null
+++ b/examples/data/matrix.json
@@ -0,0 +1,5 @@
+[
+ [1,2,3],
+ [4,5,6],
+ [7,8,9]
+]
\ No newline at end of file
diff --git a/examples/edit.php b/examples/edit.php
new file mode 100644
index 0000000..b3f2f80
--- /dev/null
+++ b/examples/edit.php
@@ -0,0 +1,75 @@
+insertAfterRow(1, [10, 11, 12]);
+
+echo $m;
+echo "\n";
+
+$m->insertBeforeRow(1, [13, 14, 15]);
+
+echo $m;
+echo "\n";
+
+$m->insertAfterColumn(1, 0);
+echo $m;
+echo "\n";
+
+$m->insertBeforeColumn(1, 0);
+echo $m;
+echo "\n";
+
+$m->removeColumn(1);
+echo $m;
+echo "\n";
+
+$m->removeRow(2);
+echo $m;
+echo "\n";
+
+$m->removeColumn(2);
+echo $m;
+echo "\n";
+
+$m->addColumn(5);
+echo $m;
+echo "\n";
+
+$m->removeColumnRange(1, 3);
+echo $m;
+echo "\n";
+
+$m->addColumn(6);
+$m->addRow([2, 2]);
+echo $m;
+echo "\n";
+
+$m->removeRowRange(1, 3);
+echo $m;
+echo "\n";
+
+$m->addColumn(7);
+$m->addRow([3, 3, 3]);
+echo $m;
+echo "\n";
+
+$m->fillRange(1, 1, 2, 2, 0);
+echo $m;
+echo "\n";
+
+$m->insertAfterColumn(1, [1, 2, 3]);
+echo $m;
+echo "\n";
+
+$m->insertBeforeColumn(1, [4, 5, 6]);
+echo $m;
+echo "\n";
diff --git a/examples/extends.php b/examples/extends.php
new file mode 100644
index 0000000..cc00563
--- /dev/null
+++ b/examples/extends.php
@@ -0,0 +1,49 @@
+ $m->get($r, 0) * $m->get($r, 2), "x");
+ }
+}
+
+class AdditionTable extends MathTable
+{
+ public function __construct($number = 1, $size = 10)
+ {
+ parent::__construct($number, $size, fn ($r, $c, $m) => $m->get($r, 0) + $m->get($r, 2), "+");
+ }
+}
+
+for ($i = 1; $i <= 10; $i++)
+{
+ echo new MultiplicationTable($i);
+ echo PHP_EOL;
+}
+
+echo PHP_EOL;
+
+for ($i = 1; $i <= 10; $i++)
+{
+ echo new AdditionTable($i);
+ echo PHP_EOL;
+}
\ No newline at end of file
diff --git a/examples/grouping.php b/examples/grouping.php
index 0076315..be4f4e7 100644
--- a/examples/grouping.php
+++ b/examples/grouping.php
@@ -19,7 +19,7 @@
]);
// Show the matrix
-echo $table->formatTXT(true);
+echo $table;
// Group by
$result = $table->groupBy([0], function($key, $group){
@@ -33,5 +33,5 @@
echo "\n";
$groupBy = new matrix(array_values($result));
$groupBy->addRow(["Product", "Total"], onTop: true);
-echo $groupBy->formatTXT(true);
+echo $groupBy;
diff --git a/examples/handlers/404.php b/examples/handlers/404.php
new file mode 100644
index 0000000..0e95d89
--- /dev/null
+++ b/examples/handlers/404.php
@@ -0,0 +1,8 @@
+console.log('{$moment} - {$url}');";
+ return true;
+};
\ No newline at end of file
diff --git a/examples/handlers/login.php b/examples/handlers/login.php
new file mode 100644
index 0000000..e2d98cf
--- /dev/null
+++ b/examples/handlers/login.php
@@ -0,0 +1,7 @@
+ isLogged()]
+]);
+
+$F_MATCH = fn ($r, $c, $m) => matchRoute($m->{$r});
+$F_PASS = fn ($r, $c, $m) => ($context->{1.1} || $m->{$r + .2}) && $m->{$r + .3};
+$F_HANDLER = fn ($r, $c, $m) => $m->{$r + .4} ? $m->{$r + .1} : 'login';
+$F_HANDLER_PATH = fn ($r, $c, $m) => __DIR__ . "/handlers/{$m->{$r + .5}}.php";
+$F_LISTENER = fn ($r, $c, matrix $m) => $m->{$r + .3} ? (require($m->{$r + .6}))() : null;
+$F_NOTHING_MATCH = fn ($r, $c, matrix $m) => array_reduce($m->vertical($c, 1, $r - 3), fn ($c, $i) => !$c && !$i, false);
+
+$routes = new matrix([
+ ["Route", "Controller", "Public", "Match", "Pass", "Handler", "Handler Path", "Listener"],
+ // -----------------------------------------------------------------------------------------------------
+ ["/", "home", true, $F_MATCH, $F_PASS, $F_HANDLER, $F_HANDLER_PATH, $F_LISTENER],
+ ["/about", "about", true, $F_MATCH, $F_PASS, $F_HANDLER, $F_HANDLER_PATH, $F_LISTENER],
+ ["/login", "login", true, $F_MATCH, $F_PASS, $F_HANDLER, $F_HANDLER_PATH, $F_LISTENER],
+ ["/admin", "admin", false, $F_MATCH, $F_PASS, $F_HANDLER, $F_HANDLER_PATH, $F_LISTENER],
+ // -----------------------------------------------------------------------------------------------------
+ ["*", "log", true, $F_MATCH, $F_PASS, $F_HANDLER, $F_HANDLER_PATH, $F_LISTENER],
+ ["*", "404", true, $F_NOTHING_MATCH, $F_PASS, $F_HANDLER, $F_HANDLER_PATH, $F_LISTENER]
+]);
+
+echo "";
diff --git a/src/matrix.php b/src/matrix.php
index c98a392..840bd61 100644
--- a/src/matrix.php
+++ b/src/matrix.php
@@ -24,22 +24,30 @@
*
* @package divengine/matrix
* @author Rafa Rodriguez @rafageist [https://rafageist.com]
- * @version 1.1.0
+ * @version 1.2.0
*
* @link https://divengine.org/docs/div-php-matrix
* @link https://github.com/divengine/matrix
*/
+use Closure;
use SimpleXMLElement;
use InvalidArgumentException;
class matrix
{
- public static $version = '1.1.0';
- private $matrix;
- private $matrix_original;
- private $evaluatedCells = [];
- private static $disableEvallAll = false;
+ public static string $version = '1.2.0';
+
+ /** @var array> $matrix */
+ private array $matrix = [];
+
+ /** @var array> $matrix_original */
+ private array $matrix_original = [];
+
+ /** @var array> $evaluatedCells */
+ private array $evaluatedCells = [];
+
+ private static bool $disableEvallAll = false;
public const FORMAT_CSV = "CSV";
public const FORMAT_XML = "XML";
public const FORMAT_JSON = "JSON";
@@ -50,37 +58,189 @@ class matrix
public const FORMAT_YAML = "YAML";
public const FORMAT_TXT = "TXT";
public const FORMAT_SQL = "SQL";
- private $defaultFormat = self::FORMAT_TXT;
- public static $workbook = [];
+ private string $defaultFormat = self::FORMAT_TXT;
+
+ /** @var array $workbook */
+ public static array $workbook = [];
/**
* Constructor
*
- * @param array $matrix
+ * @param array|object> $matrixData
*
* @throws InvalidArgumentException
*/
- public function __construct(array $matrix)
+ public function __construct(array $matrixData)
{
// convert object rows to array
- foreach ($matrix as $rowIndex => $row) {
+ /** @var array> $data */
+ $data = [];
+ foreach ($matrixData as $rowIndex => $row) {
if (is_object($row)) {
- $matrix[$rowIndex] = (array) $row;
+ $data[$rowIndex] = (array) $row;
+ } else {
+ $data[$rowIndex] = $row;
}
}
- $this->validateMatrix($matrix);
- $this->matrix = $matrix;
- $this->matrix_original = $matrix;
+ $this->validateMatrix($data);
+ $this->matrix = $data;
+ $this->matrix_original = $data;
self::$workbook[] = $this;
self::evaluateAll();
}
+ /**
+ * Create a matrix
+ *
+ * @param array|object> $matrix
+ *
+ * @return matrix
+ */
+ public static function create(array $matrix)
+ {
+ return new self($matrix);
+ }
+
+ /**
+ * Create a array from dimensions
+ *
+ * @param int $rows
+ * @param int $cols
+ * @param mixed $value
+ *
+ * @return array>
+ */
+ public static function createArrayFromDims(int $rows, int $cols, $value = null)
+ {
+ $matrix = [];
+ for ($i = 0; $i < $rows; $i++) {
+ $matrix[] = array_fill(0, $cols, $value);
+ }
+ return $matrix;
+ }
+
+ /**
+ * Create a matrix from dimensions
+ *
+ * @param int $rows
+ * @param int $cols
+ * @param mixed $value
+ *
+ * @return matrix
+ */
+ public static function dimension(int $rows, int $cols, $value = null)
+ {
+ return new self(self::createArrayFromDims($rows, $cols, $value));
+ }
+
+ /**
+ * Create a matrix from a CSV file
+ *
+ * @param string $filename
+ * @param string $delimiter
+ * @param string $enclosure
+ * @param string $escape
+ *
+ * @return matrix
+ */
+ public static function fromCSVFile(string $filename, string $delimiter = ',', string $enclosure = '"', string $escape = '\\'): matrix
+ {
+ $matrix = [];
+ if (($handle = fopen($filename, "r")) !== false) {
+ while (($data = fgetcsv($handle, 0, $delimiter, $enclosure, $escape)) !== false) {
+ $matrix[] = $data;
+ }
+ fclose($handle);
+ }
+ return new self($matrix);
+ }
+
+ /**
+ * Create a matrix from a JSON file
+ *
+ * @param string $filename
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return matrix
+ */
+ public static function fromJSONFile(string $filename): matrix
+ {
+ $json = file_get_contents($filename);
+ if ($json) {
+ $matrix = json_decode($json, true);
+
+ /** @var array|object> $matrix */
+ return new self($matrix);
+ }
+
+ throw new InvalidArgumentException('Invalid JSON file');
+ }
+
+ /**
+ * Create a matrix from a TXT file
+ *
+ * @param string $filename
+ * @param string $delimiter
+ *
+ * @return matrix
+ */
+ public static function fromTXTFile(string $filename, string $delimiter = "\t"): matrix
+ {
+ $matrix = [];
+ if (($handle = fopen($filename, "r")) !== false) {
+ while (($data = fgetcsv($handle, 0, $delimiter)) !== false) {
+ $matrix[] = $data;
+ }
+ fclose($handle);
+ }
+ return new self($matrix);
+ }
+
+ /**
+ * Create a matrix from a serialized file
+ *
+ * @param string $filename
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return matrix
+ */
+ public static function fromSerializedFile(string $filename): matrix
+ {
+ $serialized = file_get_contents($filename);
+ if ($serialized) {
+ $matrix = unserialize($serialized);
+
+ /** @var array|object> $matrix */
+ return new self($matrix);
+ }
+
+ throw new InvalidArgumentException('Invalid serialized file');
+ }
+
+ /**
+ * Create a matrix from a serialized string
+ *
+ * @param string $string
+ *
+ * @return matrix
+ */
+
+ public static function fromSerializedString(string $string): matrix
+ {
+ $matrix = unserialize($string);
+
+ /** @var array|object> $matrix */
+ return new self($matrix);
+ }
+
/**
* Validates the matrix
*
- * @param array $matrix
+ * @param array> $matrix
*
* @throws InvalidArgumentException
*
@@ -99,9 +259,12 @@ public function validateMatrix(array $matrix): void
/**
* Add a row
*
- * @param array $row
+ * @param array $row
+ * @param bool $onTop
*
- * @return int
+ * @throws InvalidArgumentException
+ *
+ * @return void
*/
public function addRow(array $row, bool $onTop = false): void
{
@@ -167,7 +330,7 @@ public function removeRow(int $index): void
/**
* Get data
*
- * @return array
+ * @return array>
*/
public function getMatrix(): array
{
@@ -177,7 +340,7 @@ public function getMatrix(): array
/**
* Validate a row
*
- * @param array $row
+ * @param array $row
*
* @throws InvalidArgumentException
*
@@ -249,33 +412,33 @@ public function formatCSV(): string
*
* @return string
*/
- public function formatXML(string $rootTag = 'root', bool $firstRowAsHeaders = false)
+ public function formatXML(string $rootTag = 'root', bool $firstRowAsHeaders = false): string
{
$xml = new SimpleXMLElement("<{$rootTag}/>");
if ($firstRowAsHeaders) {
$fields = $xml->addChild('fields');
foreach ($this->matrix[0] as $field) {
- $fields->addChild('field', $field);
+ $fields->addChild('field', $field."");
}
$data = $xml->addChild('data');
foreach (array_slice($this->matrix, 1) as $row) {
$item = $data->addChild('row');
foreach ($this->matrix[0] as $index => $field) {
- $item->addChild($field, $row[$index]);
+ $item->addChild($field."", $row[$index]."");
}
}
} else {
foreach ($this->matrix as $row) {
$item = $xml->addChild('row');
foreach ($row as $index => $field) {
- $item->addChild('field', $field);
+ $item->addChild('field', $field."");
}
}
}
- return $xml->asXML();
+ return $xml->asXML() . "";
}
/**
@@ -285,7 +448,7 @@ public function formatXML(string $rootTag = 'root', bool $firstRowAsHeaders = fa
* @param bool $asObjects
* @return string
*/
- public function formatJSON(bool $asObjects = false)
+ public function formatJSON(bool $asObjects = false): string
{
if ($asObjects) {
$result = [];
@@ -293,15 +456,15 @@ public function formatJSON(bool $asObjects = false)
// convert header names to lower snake case
$header = array_map(function ($field) {
- return str_replace(' ', '_', strtolower($field));
+ return str_replace(' ', '_', strtolower($field.""));
}, $this->matrix[0]);
// combine header names with row values
$result[] = array_combine($header, $row);
}
- return json_encode($result);
+ return json_encode($result) . "";
}
- return json_encode($this->matrix);
+ return json_encode($this->matrix) . "";
}
/**
@@ -310,9 +473,9 @@ public function formatJSON(bool $asObjects = false)
*
* @return string
*/
- public function formatSerialize()
+ public function formatSerialize(): string
{
- return serialize($this->matrix);
+ return serialize($this->matrix) . "";
}
/**
@@ -395,13 +558,22 @@ public function formatYAML(bool $firstRowAsHeaders = false): string
$array = $this->matrix;
$yaml = '';
+ $headers = [];
+
+ // by default headers are a list of consecutive numbers
+ for ($i = 0; $i < count($array[0]); $i++) {
+ $headers[] = $i;
+ }
+
if ($firstRowAsHeaders) {
// Process the first row (column names)
$headers = array_shift($array);
$yaml .= 'fields:' . PHP_EOL;
- foreach ($headers as $header) {
- $formattedHeader = str_replace(' ', '_', strtolower($header));
- $yaml .= " - $formattedHeader" . PHP_EOL;
+ if (is_array($headers)) {
+ foreach ($headers as $header) {
+ $formattedHeader = str_replace(' ', '_', strtolower($header.""));
+ $yaml .= " - $formattedHeader" . PHP_EOL;
+ }
}
}
@@ -411,9 +583,12 @@ public function formatYAML(bool $firstRowAsHeaders = false): string
$yaml .= ' - ';
$firstKey = true;
+ $i = 0;
foreach ($item as $key => $value) {
+ $i++;
// Convert the key to lowercase and replace spaces with underscores
- $formattedKey = str_replace(' ', '_', strtolower($headers[$key]));
+ $unformattedKey = $headers[$key] ?? "$i";
+ $formattedKey = str_replace(' ', '_', strtolower($unformattedKey.""));
// Add the first field on the same line with the dash, the rest aligned
if (!$firstKey) {
@@ -421,7 +596,7 @@ public function formatYAML(bool $firstRowAsHeaders = false): string
}
// Key and value for each pair
- $yaml .= "$formattedKey: $value" . PHP_EOL;
+ $yaml .= $formattedKey.": ".$value . PHP_EOL;
$firstKey = false;
}
@@ -470,7 +645,7 @@ public function formatSQL(string $tableName, bool $firstRowAsHeaders = false): s
// headers as snake case
$headers = array_map(function ($field) {
- return str_replace(' ', '_', strtolower($field));
+ return str_replace(' ', '_', strtolower($field.""));
}, $this->matrix[0]);
$sql = 'INSERT INTO ' . $tableName . ' (' . implode(', ', $headers) . ') VALUES' . PHP_EOL;
@@ -489,7 +664,7 @@ public function formatSQL(string $tableName, bool $firstRowAsHeaders = false): s
} elseif (is_object($value)) {
$row[$index] = json_encode($value);
} else {
- $row[$index] = "$value";
+ $row[$index] = $value."";
}
}
@@ -534,12 +709,14 @@ public function fillHorizontal(int $row, int $from, int $to, mixed $value): void
*
* @param int $column
* @param int $from
+ * @param int $to
+ * @param mixed $value
*
* @throws InvalidArgumentException
*
* @return void
*/
- public function fillVertical(int $column, int $from, int $to, $value): void
+ public function fillVertical(int $column, int $from, int $to, mixed $value): void
{
if ($from < 0 || $to > count($this->matrix)) {
throw new InvalidArgumentException('Invalid range');
@@ -554,6 +731,48 @@ public function fillVertical(int $column, int $from, int $to, $value): void
self::evaluateAll();
}
+ /**
+ * Fill a range
+ *
+ * @param int $rowFrom
+ * @param int $columnFrom
+ * @param int $rowTo
+ * @param int $columnTo
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return void
+ */
+ public function fillRange(int $rowFrom, int $columnFrom, int $rowTo, int $columnTo, mixed $value): void
+ {
+ $totalRows = count($this->matrix);
+
+ if ($totalRows == 0) {
+ return;
+ }
+
+ $totalColumns = count($this->matrix[0]);
+
+ (
+ $rowFrom >= 0
+ and $rowTo < $totalRows
+ and $columnFrom >= 0
+ and $columnTo < $totalColumns
+ and $rowFrom <= $rowTo
+ and $columnFrom <= $columnTo
+ ) or throw new InvalidArgumentException('Invalid range');
+
+ self::$disableEvallAll = true;
+ for ($i = $rowFrom; $i <= $rowTo; $i++) {
+ for ($j = $columnFrom; $j <= $columnTo; $j++) {
+ $this->set($i, $j, $value);
+ }
+ }
+ self::$disableEvallAll = false;
+
+ self::evaluateAll();
+ }
+
/**
* Evaluate a cell
*
@@ -603,12 +822,13 @@ public function validateCoordinates(int $row, int $column): void
*
* @param int $row
* @param int $column
+ * @param mixed $value
*
* @throws InvalidArgumentException
*
* @return void
*/
- public function set(int $row, int $column, $value): void
+ public function set(int $row, int $column, mixed $value): void
{
$this->validateCoordinates($row, $column);
@@ -689,7 +909,7 @@ public static function evaluateAll(): void
*
* @throws InvalidArgumentException
*
- * @return array
+ * @return array
*/
public function horizontal(int $row, int $from, int $to): array
{
@@ -709,7 +929,7 @@ public function horizontal(int $row, int $from, int $to): array
*
* @throws InvalidArgumentException
*
- * @return array
+ * @return array
*/
public function vertical(int $column, int $from, int $to): array
{
@@ -734,19 +954,19 @@ public function vertical(int $column, int $from, int $to): array
*
* @throws InvalidArgumentException
*
- * @return array
+ * @return array>
*/
public function range(int $rowFrom, int $columnFrom, int $rowTo, int $columnTo): array
{
(
$rowFrom >= 0
- and $rowTo <= count($this->matrix)
- and $columnFrom >= 0
- and $columnTo <= count($this->matrix[0])
- and $rowFrom <= $rowTo
- and $columnFrom <= $columnTo
+ and $rowTo <= count($this->matrix)
+ and $columnFrom >= 0
+ and $columnTo <= count($this->matrix[0])
+ and $rowFrom <= $rowTo
+ and $columnFrom <= $columnTo
)
- or throw new InvalidArgumentException('Invalid range');
+ or throw new InvalidArgumentException('Invalid range');
$result = [];
for ($i = $rowFrom; $i <= $rowTo; $i++) {
@@ -758,10 +978,11 @@ public function range(int $rowFrom, int $columnFrom, int $rowTo, int $columnTo):
/**
* Group by a column
*
- * @param int $column
+ * @param array $columns
+ * @param \Closure $aggregate
* @param bool $firstRowAsHeaders
*
- * @return array
+ * @return array>>
*/
public function groupBy(array $columns, \Closure $aggregate = null, bool $firstRowAsHeaders = false): array
{
@@ -769,7 +990,7 @@ public function groupBy(array $columns, \Closure $aggregate = null, bool $firstR
$result = [];
if ($firstRowAsHeaders) {
- $headers = array_shift($data);
+ array_shift($data);
}
foreach ($data as $row) {
@@ -794,22 +1015,32 @@ public function groupBy(array $columns, \Closure $aggregate = null, bool $firstR
return $result;
}
- public function getTotalRows()
+ /**
+ * Get the total rows
+ *
+ * @return int
+ */
+ public function getTotalRows(): int
{
return count($this->matrix);
}
- public function getTotalColumns()
+ /**
+ * Get the total columns
+ *
+ * @return int
+ */
+ public function getTotalColumns(): int
{
return count($this->matrix[0]);
- }
+ }
/**
* Get a row
*
* @param int $row
*
- * @return array
+ * @return array
*/
public function getRow(int $row): array
{
@@ -821,7 +1052,7 @@ public function getRow(int $row): array
*
* @param int $column
*
- * @return array
+ * @return array
*/
public function getColumn(int $column): array
{
@@ -837,7 +1068,7 @@ public function getColumn(int $column): array
*
* @param int $row
*
- * @return array
+ * @return array
*/
public function getRowWithFormulas(int $row): array
{
@@ -849,7 +1080,7 @@ public function getRowWithFormulas(int $row): array
*
* @param int $column
*
- * @return array
+ * @return array
*/
public function getColumnWithFormulas(int $column): array
{
@@ -919,4 +1150,218 @@ public function __toString()
{
return $this->format($this->defaultFormat);
}
+
+ /**
+ * Magic method to get a property
+ *
+ * @param string $name
+ *
+ * @return mixed
+ */
+ public function __get($name): mixed
+ {
+ if ($name == "rows") {
+ return $this->getTotalRows();
+ }
+
+ if ($name == "columns") {
+ return $this->getTotalColumns();
+ }
+
+ if (is_numeric($name)) {
+ $pos = $name * 1;
+ $name = explode(".", $name);
+ $row = intval($name[0]);
+ $column = intval($name[1] ?? 0);
+
+ if ($pos < 0) {
+ $row = $this->getTotalRows() + $row;
+ }
+
+ return $this->get($row, $column);
+ }
+
+ if (property_exists($this, $name)) {
+ return $this->$name;
+ }
+
+ throw new InvalidArgumentException('Invalid property');
+ }
+
+ /**
+ * Magic method to set a property
+ *
+ * @param string $name
+ * @param mixed $value
+ *
+ * @return void
+ */
+ public function __set($name, $value): void
+ {
+ if (is_numeric($name)) {
+ $pos = $name * 1;
+ $name = explode(".", $name);
+ $row = intval($name[0]);
+ $column = intval($name[1] ?? 0);
+
+ if ($pos < 0) {
+ $row = $this->getTotalRows() + $row;
+ }
+
+ $this->set($row, $column, $value);
+ return;
+ }
+
+ if (property_exists($this, $name)) {
+ $this->$name = $value;
+ return;
+ }
+
+ throw new InvalidArgumentException('Invalid property');
+ }
+
+ /**
+ * Insert a row before a row
+ *
+ * @param int $row
+ * @param array $data
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return void
+ */
+ public function insertBeforeRow(int $row, array $data): void
+ {
+ if (!$this->validateRow($data)) {
+ throw new InvalidArgumentException('Row does not have the same number of elements as the first row');
+ }
+
+ array_splice($this->matrix, $row, 0, [$data]);
+ array_splice($this->matrix_original, $row, 0, [$data]);
+
+ self::evaluateAll();
+ }
+
+ /**
+ * Insert a row after a row
+ *
+ * @param int $row
+ * @param array $data
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return void
+ */
+ public function insertAfterRow(int $row, array $data): void
+ {
+ if (!$this->validateRow($data)) {
+ throw new InvalidArgumentException('Row does not have the same number of elements as the first row');
+ }
+
+ array_splice($this->matrix, $row + 1, 0, [$data]);
+ array_splice($this->matrix_original, $row + 1, 0, [$data]);
+
+ self::evaluateAll();
+ }
+
+ /**
+ * Insert a column before a column
+ *
+ * @param int $column
+ * @param mixed $value
+ *
+ * @return void
+ */
+ public function insertBeforeColumn(int $column, mixed $value = null): void
+ {
+ if (!is_array($value)) {
+ $value = array_fill(0, count($this->matrix), $value);
+ $value = (array) $value;
+ }
+
+ $i = 0;
+ foreach ($this->matrix as $rowIndex => $row) {
+ array_splice($this->matrix[$rowIndex], $column, 0, [$value[$i]]);
+ array_splice($this->matrix_original[$rowIndex], $column, 0, [$value[$i]]);
+ $i++;
+ }
+
+ self::evaluateAll();
+ }
+
+ /**
+ * Insert a column after a column
+ *
+ * @param int $column
+ * @param mixed $value
+ *
+ * @return void
+ */
+ public function insertAfterColumn(int $column, $value = null): void
+ {
+ if (!is_array($value)) {
+ $value = array_fill(0, count($this->matrix), $value);
+ $value = (array) $value;
+ }
+
+ $i = 0;
+ foreach ($this->matrix as $rowIndex => $row) {
+ array_splice($this->matrix[$rowIndex], $column + 1, 0, [$value[$i]]);
+ array_splice($this->matrix_original[$rowIndex], $column + 1, 0, [$value[$i]]);
+ $i++;
+ }
+
+ self::evaluateAll();
+ }
+
+ /**
+ * Remove a column
+ *
+ * @param int $column
+ *
+ * @return void
+ */
+ public function removeColumn(int $column): void
+ {
+ foreach ($this->matrix as $rowIndex => $row) {
+ array_splice($this->matrix[$rowIndex], $column, 1);
+ array_splice($this->matrix_original[$rowIndex], $column, 1);
+ }
+
+ self::evaluateAll();
+ }
+
+ /**
+ * Remove a column
+ *
+ * @param int $from
+ * @param int $to
+ *
+ * @return void
+ */
+ public function removeColumnRange(int $from, int $to): void
+ {
+ foreach ($this->matrix as $rowIndex => $row) {
+ array_splice($this->matrix[$rowIndex], $from, $to - $from + 1);
+ array_splice($this->matrix_original[$rowIndex], $from, $to - $from + 1);
+ }
+
+ self::evaluateAll();
+ }
+
+ /**
+ * Remove a row
+ *
+ * @param int $from
+ * @param int $to
+ *
+ * @return void
+ */
+ public function removeRowRange(int $from, int $to): void
+ {
+ array_splice($this->matrix, $from, $to - $from + 1);
+ array_splice($this->matrix_original, $from, $to - $from + 1);
+
+ self::evaluateAll();
+ }
}