do not encrypt column needed for ORDER BY clause
This commit is contained in:
parent
9196c557f8
commit
98d7307860
|
@ -153,10 +153,10 @@ class ColumnMeta
|
|||
public $dataType; //a string that includes the size of the type if necessary (e.g., decimal(10,5))
|
||||
public $colName; //column name
|
||||
public $options; //a string that is null by default (e.g. NOT NULL Identity (1,1) )
|
||||
public $encType; //randomized or deterministic; default is deterministic
|
||||
public $forceEncrypt; //force encryption on a datatype no supported by Column Encrypton
|
||||
public $encType; //randomized, deterministic, or normal; default is null
|
||||
public $forceEncrypt; //force encryption on a datatype not supported by Column Encrypton
|
||||
|
||||
public function __construct($dataType, $colName = null, $options = null, $encType = "deterministic", $forceEncrypt = false)
|
||||
public function __construct($dataType, $colName = null, $options = null, $encType = null, $forceEncrypt = false)
|
||||
{
|
||||
if (is_null($colName)) {
|
||||
$this->colName = getDefaultColName($dataType);
|
||||
|
@ -165,6 +165,15 @@ class ColumnMeta
|
|||
}
|
||||
$this->dataType = $dataType;
|
||||
$this->options = $options;
|
||||
if (is_null($encType)) {
|
||||
if (isColEncrypted()) {
|
||||
$this->encType = "deterministic";
|
||||
} else {
|
||||
$this->encType = "normal";
|
||||
}
|
||||
} else {
|
||||
$this->encType = $encType;
|
||||
}
|
||||
$this->encType = $encType;
|
||||
$this->forceEncrypt = $forceEncrypt;
|
||||
}
|
||||
|
@ -173,11 +182,11 @@ class ColumnMeta
|
|||
*/
|
||||
public function getColDef()
|
||||
{
|
||||
//return getColDef($this->colName, $this->dataType, $this->options, $this->encType);
|
||||
$append = " ";
|
||||
|
||||
// an identity column is not encrypted because a select query with identity column as the where clause is often run and the user want to have to bind parameter every time
|
||||
if (isColEncrypted() && $this->isEncryptableType() && stripos($this->options, "identity") === false && stripos($this->options, "rowguidcol") === false) {
|
||||
if (isColEncrypted() && ($this->encType == "deterministic" || $this->encType == "ramdomized") && $this->isEncryptableType()
|
||||
&& stripos($this->options, "identity") === false && stripos($this->options, "rowguidcol") === false) {
|
||||
$cekName = getCekName();
|
||||
if (stripos($this->dataType, "char") !== false) {
|
||||
$append .= "COLLATE Latin1_General_BIN2 ";
|
||||
|
|
|
@ -48,7 +48,7 @@ try {
|
|||
$num = $stmt2->fetchColumn();
|
||||
echo "There are $num rows in the table.\n";
|
||||
|
||||
createTable($conn1, $tableName, array(new ColumnMeta("int", "idx", "NOT NULL PRIMARY KEY"), "txt" => "varchar(20)"));
|
||||
createTable($conn1, $tableName, array(new ColumnMeta("int", "idx", "NOT NULL PRIMARY KEY", "normal"), "txt" => "varchar(20)"));
|
||||
insertRow($conn1, $tableName, array("idx" => 0, "txt" => 'String0'));
|
||||
insertRow($conn1, $tableName, array("idx" => 1, "txt" => 'String1'));
|
||||
insertRow($conn1, $tableName, array("idx" => 2, "txt" => 'String2'));
|
||||
|
@ -59,18 +59,9 @@ try {
|
|||
var_dump($stmt1->fetchColumn());
|
||||
unset($stmt1);
|
||||
|
||||
if (!isColEncrypted()) {
|
||||
$stmt1 = $conn1->prepare("SELECT idx, txt FROM [$tableName] ORDER BY idx");
|
||||
} else {
|
||||
// ORDER BY does not work on encrypted columns
|
||||
$stmt1 = $conn1->prepare("SELECT idx, txt FROM [$tableName]");
|
||||
}
|
||||
$stmt1 = $conn1->prepare("SELECT idx, txt FROM [$tableName] ORDER BY idx");
|
||||
$stmt1->execute();
|
||||
$data = $stmt1->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_UNIQUE);
|
||||
// needs to order the result set manually as ORDER BY does not work properly on encrypted columns
|
||||
if (isColEncrypted()) {
|
||||
ksort($data);
|
||||
}
|
||||
var_dump($data);
|
||||
|
||||
echo "===WHILE===\n";
|
||||
|
@ -100,7 +91,7 @@ try {
|
|||
|
||||
var_dump($stmt2->execute());
|
||||
if ($idx == 0) { // bindColumn()s after execute() has been called at least once
|
||||
$stmt2->bindColumn('txt', $txtCol);
|
||||
$stmt2->bindColumn('txt', $col);
|
||||
}
|
||||
var_dump($stmt2->fetch(PDO::FETCH_BOUND));
|
||||
$stmt2->closeCursor();
|
||||
|
@ -112,23 +103,23 @@ try {
|
|||
var_dump($stmt3->fetch(PDO::FETCH_BOUND));
|
||||
$stmt3->closeCursor();
|
||||
|
||||
var_dump(array($idxCol=>$txtCol));
|
||||
var_dump(array($idxCol=>$col));
|
||||
}
|
||||
|
||||
echo "===REBIND/SAME===\n";
|
||||
|
||||
$stmt3->bindColumn('idx', $idxCol);
|
||||
$stmt3->bindColumn('idx', $col);
|
||||
foreach ($data as $idx => $txt) {
|
||||
var_dump(array($idx=>$txt));
|
||||
var_dump($stmt2->execute());
|
||||
var_dump($stmt2->fetch(PDO::FETCH_BOUND));
|
||||
$stmt2->closeCursor();
|
||||
|
||||
var_dump($idxCol);
|
||||
var_dump($col);
|
||||
var_dump($stmt3->execute());
|
||||
var_dump($stmt3->fetch(PDO::FETCH_BOUND));
|
||||
$stmt3->closeCursor();
|
||||
var_dump($idxCol);
|
||||
var_dump($col);
|
||||
}
|
||||
|
||||
echo "===REBIND/CONFLICT===\n";
|
||||
|
|
|
@ -15,7 +15,7 @@ try {
|
|||
|
||||
// Prepare test table
|
||||
$tableName = "pdo_test_table";
|
||||
createTable($conn1, $tableName, array(new ColumnMeta("int", "idx", "NOT NULL PRIMARY KEY"), "txt" => "varchar(20)"));
|
||||
createTable($conn1, $tableName, array(new ColumnMeta("int", "idx", "NOT NULL PRIMARY KEY", "normal"), "txt" => "varchar(20)"));
|
||||
insertRow($conn1, $tableName, array("idx" => 0, "txt" => "String0"));
|
||||
insertRow($conn1, $tableName, array("idx" => 1, "txt" => "String1"));
|
||||
insertRow($conn1, $tableName, array("idx" => 2, "txt" => "String2"));
|
||||
|
@ -28,36 +28,17 @@ try {
|
|||
unset($stmt1);
|
||||
|
||||
logInfo(2, "Testing fetchAll() ...");
|
||||
// ORDER BY doesn't work for encrypted columns
|
||||
// need to fetch all rows first then sort and print
|
||||
if (!isColEncrypted()) {
|
||||
$stmt1 = $conn1->prepare("SELECT idx, txt FROM [$tableName] ORDER BY idx");
|
||||
} else {
|
||||
$stmt1 = $conn1->prepare("SELECT idx, txt FROM [$tableName]");
|
||||
}
|
||||
$stmt1 = $conn1->prepare("SELECT idx, txt FROM [$tableName] ORDER BY idx");
|
||||
$stmt1->execute();
|
||||
$data = $stmt1->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_UNIQUE);
|
||||
if (isColEncrypted()) {
|
||||
sort($data);
|
||||
}
|
||||
var_dump($data);
|
||||
|
||||
logInfo(3, "Testing bindColumn() ...");
|
||||
$stmt1->bindColumn('idx', $idx);
|
||||
$stmt1->bindColumn('txt', $txt);
|
||||
$stmt1->execute();
|
||||
$idxArray = array();
|
||||
$txtArray = array();
|
||||
while ($stmt1->fetch(PDO::FETCH_BOUND)) {
|
||||
array_push($idxArray, $idx);
|
||||
array_push($txtArray, $txt);
|
||||
}
|
||||
if (isColEncrypted()) {
|
||||
sort($idxArray);
|
||||
sort($txtArray);
|
||||
}
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
var_dump(array($idxArray[$i] => $txtArray[$i]));
|
||||
var_dump(array($idx=>$txt));
|
||||
}
|
||||
|
||||
logInfo(4, "Testing bindColumn() with data check ...");
|
||||
|
@ -74,27 +55,13 @@ try {
|
|||
$stmt1->execute();
|
||||
while ($stmt1->fetch(PDO::FETCH_BOUND)) {
|
||||
$data[] = array('id' => $id, 'val' => $val);
|
||||
printf("id = %s (%s) / val = %s (%s)\n",
|
||||
var_export($id, true), gettype($id),
|
||||
var_export($val, true), gettype($val));
|
||||
}
|
||||
$sortedData = $data;
|
||||
if (isColEncrypted()) {
|
||||
sort($sortedData);
|
||||
}
|
||||
foreach ($sortedData as $d) {
|
||||
printf(
|
||||
"id = %s (%s) / val = %s (%s)\n",
|
||||
var_export($d['id'], true),
|
||||
gettype($d['id']),
|
||||
var_export($d['val'], true),
|
||||
gettype($d['val'])
|
||||
);
|
||||
}
|
||||
|
||||
unset($stmt1);
|
||||
if (!isColEncrypted()) {
|
||||
$stmt1 = $conn1->query("SELECT idx, txt FROM [$tableName] ORDER BY idx");
|
||||
} else {
|
||||
$stmt1 = $conn1->query("SELECT idx, txt FROM [$tableName]");
|
||||
}
|
||||
|
||||
$stmt1 = $conn1->query("SELECT idx, txt FROM [$tableName] ORDER BY idx");
|
||||
while ($row = $stmt1->fetch(PDO::FETCH_ASSOC)) {
|
||||
if ($row['idx'] != $data[$index]['id']) {
|
||||
logInfo(6, "Data corruption for integer column in row $index");
|
||||
|
|
|
@ -15,7 +15,7 @@ try {
|
|||
|
||||
// Prepare test table
|
||||
$tableName = "pdo_test_table";
|
||||
createTable($conn1, $tableName, array(new ColumnMeta("int", "id", "NOT NULL PRIMARY KEY"), "label" => "char(1)"));
|
||||
createTable($conn1, $tableName, array(new ColumnMeta("int", "id", "NOT NULL PRIMARY KEY", "normal"), "label" => "char(1)"));
|
||||
insertRow($conn1, $tableName, array("id" => 1, "label" => "a"));
|
||||
insertRow($conn1, $tableName, array("id" => 2, "label" => "b"));
|
||||
insertRow($conn1, $tableName, array("id" => 3, "label" => "c"));
|
||||
|
@ -27,36 +27,13 @@ try {
|
|||
$midRow = 4;
|
||||
|
||||
// Check bind column
|
||||
// order by does not work for encrypted columns
|
||||
if (!isColEncrypted()) {
|
||||
$tsql1 = "SELECT TOP($rowCount) id, label FROM [$tableName] ORDER BY id ASC";
|
||||
} else {
|
||||
$tsql1 = "SELECT TOP($rowCount) id, label FROM [$tableName]";
|
||||
}
|
||||
$data1 = bindColumn($conn1, $tsql1);
|
||||
checkBind($conn1, $tsql1, $data1);
|
||||
$tsql1 = "SELECT TOP($rowCount) id, label FROM [$tableName] ORDER BY id ASC";
|
||||
$data = bindColumn($conn1, $tsql1);
|
||||
checkBind($conn1, $tsql1, $data);
|
||||
|
||||
if (!isColEncrypted()) {
|
||||
$tsql2 = "SELECT TOP($rowCount) id, label FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY id ASC) as row FROM [$tableName]) [$tableName] WHERE row >= $midRow";
|
||||
} else {
|
||||
$tsql2 = "SELECT TOP($rowCount) id, label FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY (SELECT 1)) as row FROM [$tableName]) [$tableName] WHERE row >= $midRow";
|
||||
}
|
||||
$data2 = bindColumn($conn1, $tsql2);
|
||||
checkBind($conn1, $tsql2, $data2);
|
||||
|
||||
$data = array_merge($data1, $data2);
|
||||
if (isColEncrypted()) {
|
||||
sort($data);
|
||||
}
|
||||
foreach ($data as $d) {
|
||||
printf(
|
||||
"id = %s (%s) / label = %s (%s)\n",
|
||||
var_export($d['id'], true),
|
||||
gettype($d['id']),
|
||||
var_export($d['label'], true),
|
||||
gettype($d['label'])
|
||||
);
|
||||
}
|
||||
$tsql2 = "SELECT TOP($rowCount) id, label FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY id ASC) as row FROM [$tableName]) [$tableName] WHERE row >= $midRow";
|
||||
$data = bindColumn($conn1, $tsql2);
|
||||
checkBind($conn1, $tsql2, $data);
|
||||
|
||||
// Cleanup
|
||||
dropTable($conn1, $tableName);
|
||||
|
@ -80,6 +57,9 @@ function bindColumn($conn, $tsql)
|
|||
logInfo(1, "Cannot bind string column");
|
||||
}
|
||||
while ($stmt->fetch(PDO::FETCH_BOUND)) {
|
||||
printf("id = %s (%s) / label = %s (%s)\n",
|
||||
var_export($id, true), gettype($id),
|
||||
var_export($label, true), gettype($label));
|
||||
$data[] = array('id' => $id, 'label' => $label);
|
||||
}
|
||||
unset($stmt);
|
||||
|
|
|
@ -16,7 +16,7 @@ try {
|
|||
// Prepare test table
|
||||
$dataCols = "id, label";
|
||||
$tableName = "pdo_test_table";
|
||||
createTable($conn1, $tableName, array(new ColumnMeta("int", "id", "NOT NULL PRIMARY KEY"), "label" => "char(1)"));
|
||||
createTable($conn1, $tableName, array(new ColumnMeta("int", "id", "NOT NULL PRIMARY KEY", "normal"), "label" => "char(1)"));
|
||||
insertRow($conn1, $tableName, array("id" => 1, "label" => 'a'));
|
||||
insertRow($conn1, $tableName, array("id" => 2, "label" => 'b'));
|
||||
insertRow($conn1, $tableName, array("id" => 3, "label" => 'c'));
|
||||
|
@ -28,13 +28,8 @@ try {
|
|||
$label = null;
|
||||
|
||||
// Bind param @ SELECT
|
||||
if (!isColEncrypted()) {
|
||||
$tsql1 = "SELECT TOP(2) id, label FROM [$tableName] WHERE id > ? ORDER BY id ASC";
|
||||
$value1 = array(1 => 0);
|
||||
} else {
|
||||
$tsql1 = "SELECT id, label FROM $tableName WHERE id = ? OR id = ?";
|
||||
$value1 = array(1 => 1, 2 => 2);
|
||||
}
|
||||
$tsql1 = "SELECT TOP(2) id, label FROM [$tableName] WHERE id > ? ORDER BY id ASC";
|
||||
$value1 = 0;
|
||||
$stmt1 = $conn1->prepare($tsql1);
|
||||
bindParam(1, $stmt1, $value1);
|
||||
execStmt(1, $stmt1);
|
||||
|
@ -45,13 +40,8 @@ try {
|
|||
unset($stmt1);
|
||||
|
||||
// Bind param @ INSERT
|
||||
if (!isColEncrypted()) {
|
||||
$tsql2 = "INSERT INTO [$tableName](id, label) VALUES (100, ?)";
|
||||
$value2 = array(1 => null);
|
||||
} else {
|
||||
$tsql2 = "INSERT INTO [$tableName](id, label) VALUES (?, ?)";
|
||||
$value2 = array(1 => 100, 2 => null);
|
||||
}
|
||||
$tsql2 = "INSERT INTO [$tableName](id, label) VALUES (100, ?)";
|
||||
$value2 = null;
|
||||
$stmt1 = $conn1->prepare($tsql2);
|
||||
bindParam(2, $stmt1, $value2);
|
||||
execStmt(2, $stmt1);
|
||||
|
@ -72,10 +62,10 @@ try {
|
|||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
function bindParam($offset, $stmt, $value)
|
||||
function bindParam($offset, $stmt, &$value)
|
||||
{
|
||||
foreach ($value as $key => &$val) {
|
||||
$stmt->bindParam($key, $val);
|
||||
if (!$stmt->bindParam(1, $value)) {
|
||||
logInfo($offset,"Cannot bind parameter");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ try {
|
|||
|
||||
// Prepare test table
|
||||
$tableName = "pdo_test_table";
|
||||
createTable($conn1, $tableName, array(new ColumnMeta("int", "id", "NOT NULL PRIMARY KEY"), "label" => "char(1)"));
|
||||
createTable($conn1, $tableName, array(new ColumnMeta("int", "id", "NOT NULL PRIMARY KEY", "normal"), "label" => "char(1)"));
|
||||
insertRow($conn1, $tableName, array("id" => 1, "label" => "a"));
|
||||
insertRow($conn1, $tableName, array("id" => 2, "label" => "b"));
|
||||
insertRow($conn1, $tableName, array("id" => 3, "label" => "c"));
|
||||
|
@ -27,34 +27,18 @@ try {
|
|||
$label = null;
|
||||
|
||||
// Check different value bind modes
|
||||
if (!isColEncrypted()) {
|
||||
$tsql1 = "SELECT TOP(2) id, label FROM [$tableName] WHERE id > ? ORDER BY id ASC";
|
||||
} else {
|
||||
$tsql1 = "SELECT id, label FROM [$tableName] WHERE id = ? OR id = ?";
|
||||
}
|
||||
$tsql1 = "SELECT TOP(2) id, label FROM [$tableName] WHERE id > ? ORDER BY id ASC";
|
||||
$stmt1 = $conn1->prepare($tsql1);
|
||||
|
||||
printf("Binding value and not variable...\n");
|
||||
if (!isColEncrypted()) {
|
||||
bindValue(1, 1, $stmt1, 0);
|
||||
} else {
|
||||
bindValue(1, 1, $stmt1, 1);
|
||||
bindValue(1, 2, $stmt1, 2);
|
||||
}
|
||||
bindValue(1, $stmt1, 0);
|
||||
execStmt(1, $stmt1);
|
||||
bindColumn(1, $stmt1, $id, $label);
|
||||
fetchBound($stmt1, $id, $label);
|
||||
|
||||
printf("Binding variable...\n");
|
||||
$var1 = 0;
|
||||
if (!isColEncrypted()) {
|
||||
bindVar(2, 1, $stmt1, $var1);
|
||||
} else {
|
||||
$var11 = $var1 + 1;
|
||||
$var12 = $var1 + 2;
|
||||
bindVar(2, 1, $stmt1, $var11);
|
||||
bindVar(2, 2, $stmt1, $var12);
|
||||
}
|
||||
bindVar(2, $stmt1, $var1);
|
||||
execStmt(2, $stmt1);
|
||||
bindColumn(2, $stmt1, $id, $label);
|
||||
fetchBound($stmt1, $id, $label);
|
||||
|
@ -62,33 +46,17 @@ try {
|
|||
printf("Binding variable which references another variable...\n");
|
||||
$var2 = 0;
|
||||
$var_ref = &$var2;
|
||||
if (!isColEncrypted()) {
|
||||
bindVar(3, 1, $stmt1, $var_ref);
|
||||
} else {
|
||||
$var21 = $var2 + 1;
|
||||
$var22 = $var2 + 2;
|
||||
$var_ref1 = &$var21;
|
||||
$var_ref2 = &$var22;
|
||||
bindVar(3, 1, $stmt1, $var_ref1);
|
||||
bindVar(3, 2, $stmt1, $var_ref2);
|
||||
}
|
||||
bindVar(3, $stmt1, $var_ref);
|
||||
execStmt(3, $stmt1);
|
||||
bindColumn(3, $stmt1, $id, $label);
|
||||
fetchBound($stmt1, $id, $label);
|
||||
unset($stmt1);
|
||||
|
||||
if (!isColEncrypted()) {
|
||||
$tsql2 = "SELECT TOP(2) id, label FROM [$tableName] WHERE id > ? AND id <= ? ORDER BY id ASC";
|
||||
} else {
|
||||
$tsql2 = "SELECT id, label FROM [$tableName] WHERE id = ? OR id = ?";
|
||||
}
|
||||
$tsql2 = "SELECT TOP(2) id, label FROM [$tableName] WHERE id > ? AND id <= ? ORDER BY id ASC";
|
||||
$stmt1 = $conn1->prepare($tsql2);
|
||||
|
||||
printf("Binding a variable and a value...\n");
|
||||
$var3 = 0;
|
||||
if (isColEncrypted()) {
|
||||
$var3++;
|
||||
}
|
||||
bindMixed(4, $stmt1, $var3, 2);
|
||||
execStmt(4, $stmt1);
|
||||
bindColumn(4, $stmt1, $id, $label);
|
||||
|
@ -96,9 +64,6 @@ try {
|
|||
|
||||
printf("Binding a variable to two placeholders and changing the variable value in between the binds...\n");
|
||||
$var4 = 0;
|
||||
if (isColEncrypted()) {
|
||||
$var4++;
|
||||
}
|
||||
$var5 = 2;
|
||||
bindPlaceholder(5, $stmt1, $var4, $var5);
|
||||
execStmt(5, $stmt1);
|
||||
|
@ -114,16 +79,16 @@ try {
|
|||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
function bindValue($offset, $index, $stmt, $value)
|
||||
function bindValue($offset, $stmt, $value)
|
||||
{
|
||||
if (!$stmt->bindValue($index, $value)) {
|
||||
if (!$stmt->bindValue(1, $value)) {
|
||||
logInfo($offset, "Cannot bind value");
|
||||
}
|
||||
}
|
||||
|
||||
function bindVar($offset, $index, $stmt, &$var)
|
||||
function bindVar($offset, $stmt, &$var)
|
||||
{
|
||||
if (!$stmt->bindValue($index, $var)) {
|
||||
if (!$stmt->bindValue(1, $var)) {
|
||||
logInfo($offset, "Cannot bind variable");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,10 @@ function katmaiBasicTypes($conn)
|
|||
"c5_geometry" => '0000000001140000000000803e401f85eb51b81ee5bf48e17a14ae073f4052b81e85eb51d8bf',
|
||||
"c6_hierarchyid" => '5bc0',
|
||||
"c7_uniqueidentifier" => '35413141383846372d333734392d343641332d384137412d454641453733454645383846');
|
||||
|
||||
if (isColEncrypted()) {
|
||||
// remove these types from tests because these types require direct query for the data to be inserted
|
||||
// and the insertRow common function uses bind parameters to insertion when column encryption is enabled
|
||||
$toRemove = array("c4_geography", "c5_geometry", "c6_hierarchyid");
|
||||
foreach ($toRemove as $key) {
|
||||
unset($dataTypes[$key]);
|
||||
|
|
|
@ -78,23 +78,13 @@ function updateFood($conn, $tableName, $id, $food, $category)
|
|||
|
||||
function fetchRows($conn, $tableName)
|
||||
{
|
||||
if (!isColEncrypted()) {
|
||||
$query = "SELECT * FROM $tableName ORDER BY id";
|
||||
} else {
|
||||
$query = "SELECT * FROM $tableName";
|
||||
}
|
||||
$query = "SELECT * FROM $tableName ORDER BY id";
|
||||
$stmt = $conn->query($query);
|
||||
|
||||
$stmt->setFetchMode(PDO::FETCH_CLASS, 'Food');
|
||||
$foodArray = array();
|
||||
while ($food = $stmt->fetch()) {
|
||||
array_push($foodArray, $food);
|
||||
}
|
||||
if (isColEncrypted()) {
|
||||
sort($foodArray);
|
||||
}
|
||||
foreach ($foodArray as $food) {
|
||||
echo "ID: " . $food->id . " ";
|
||||
echo "ID: " . $food->id . " ";
|
||||
echo $food->getFood() . ", ";
|
||||
echo $food->getcategory() . "\n";
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue