| 1 | <?php |
||
| 2 | |||
| 3 | namespace Lagdo\DbAdmin\Db\Page\Dml; |
||
| 4 | |||
| 5 | use Lagdo\DbAdmin\Db\Page\AppPage; |
||
| 6 | use Lagdo\DbAdmin\Driver\Utils\Utils; |
||
| 7 | use Lagdo\DbAdmin\Driver\DriverInterface; |
||
| 8 | use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity; |
||
| 9 | use Lagdo\DbAdmin\Driver\Entity\UserTypeEntity; |
||
| 10 | |||
| 11 | use function bin2hex; |
||
| 12 | use function implode; |
||
| 13 | use function is_array; |
||
| 14 | use function is_bool; |
||
| 15 | use function json_encode; |
||
| 16 | use function preg_match; |
||
| 17 | |||
| 18 | /** |
||
| 19 | * Writes data in the user forms for data row insert and update. |
||
| 20 | */ |
||
| 21 | class DataFieldValue |
||
| 22 | { |
||
| 23 | /** |
||
| 24 | * @var bool |
||
| 25 | */ |
||
| 26 | private bool $isUpdate = false; |
||
| 27 | |||
| 28 | /** |
||
| 29 | * @var array<UserTypeEntity> |
||
| 30 | */ |
||
| 31 | private array $userTypes; |
||
| 32 | |||
| 33 | /** |
||
| 34 | * The constructor |
||
| 35 | * |
||
| 36 | * @param AppPage $page |
||
| 37 | * @param DriverInterface $driver |
||
| 38 | * @param Utils $utils |
||
| 39 | * @param string $action |
||
| 40 | * @param string $operation |
||
| 41 | */ |
||
| 42 | public function __construct(private AppPage $page, private DriverInterface $driver, |
||
| 43 | private Utils $utils, private string $action, private string $operation) |
||
| 44 | { |
||
| 45 | $this->isUpdate = $operation === 'update'; |
||
| 46 | $this->userTypes = $this->driver->userTypes(true); |
||
| 47 | } |
||
| 48 | |||
| 49 | /** |
||
| 50 | * @param array $names |
||
| 51 | * @param array $functions |
||
| 52 | * @param bool $addSql |
||
| 53 | * @param TableFieldEntity $field |
||
| 54 | * |
||
| 55 | * @return array |
||
| 56 | */ |
||
| 57 | private function addEditFunctions(array $names, array $functions, TableFieldEntity $field): array |
||
| 58 | { |
||
| 59 | foreach ($functions as $pattern => $_functions) { |
||
| 60 | if (!$pattern || preg_match("~$pattern~", $field->type)) { |
||
| 61 | $names = [...$names, ...$_functions]; // Array merge |
||
| 62 | } |
||
| 63 | } |
||
| 64 | return $names; |
||
| 65 | } |
||
| 66 | |||
| 67 | /** |
||
| 68 | * Functions displayed in edit form |
||
| 69 | * function editFunctions() in adminer.inc.php |
||
| 70 | * |
||
| 71 | * @param TableFieldEntity $field Single field from fields() |
||
| 72 | * |
||
| 73 | * @return array |
||
| 74 | */ |
||
| 75 | private function editFunctions(TableFieldEntity $field): array |
||
| 76 | { |
||
| 77 | if ($field->autoIncrement && !$this->isUpdate) { |
||
| 78 | return [$this->utils->trans->lang('Auto Increment')]; |
||
| 79 | } |
||
| 80 | |||
| 81 | $names = $field->null ? ['NULL', ''] : ['']; |
||
| 82 | $functions = $this->driver->insertFunctions(); |
||
| 83 | $names = $this->addEditFunctions($names, $functions, $field); |
||
| 84 | |||
| 85 | $functions = $this->driver->editFunctions(); |
||
| 86 | if (/*!isset($this->utils->input->values['call']) &&*/ $this->isUpdate) { // relative functions |
||
| 87 | $names = $this->addEditFunctions($names, $functions, $field); |
||
| 88 | } |
||
| 89 | |||
| 90 | $structuredTypes = $this->driver->structuredTypes(); |
||
| 91 | $userTypes = $structuredTypes[$this->utils->trans->lang('User types')] ?? []; |
||
| 92 | if ($functions && !preg_match('~set|bool~', $field->type) && |
||
|
0 ignored issues
–
show
|
|||
| 93 | !$this->utils->isBlob($field, $userTypes)) { |
||
| 94 | $names[] = 'SQL'; |
||
| 95 | } |
||
| 96 | |||
| 97 | // $dbFunctions = [ |
||
| 98 | // 'insert' => $this->driver->insertFunctions(), |
||
| 99 | // 'edit' => $this->driver->editFunctions(), |
||
| 100 | // ]; |
||
| 101 | // foreach ($dbFunctions as $key => $functions) { |
||
| 102 | // if ($key === 'insert' || (!$isCall && $this->isUpdate)) { // relative functions |
||
| 103 | // foreach ($functions as $pattern => $value) { |
||
| 104 | // if (!$pattern || preg_match("~$pattern~", $field->type)) { |
||
| 105 | // $names = [...$names, ...$value]; // Array merge |
||
| 106 | // } |
||
| 107 | // } |
||
| 108 | // } |
||
| 109 | // if ($key === 'edit' && !preg_match('~set|bool~', $field->type) && !$this->utils->isBlob($field, $userTypes)) { |
||
| 110 | // $names[] = 'SQL'; |
||
| 111 | // } |
||
| 112 | // } |
||
| 113 | |||
| 114 | return $names; |
||
| 115 | } |
||
| 116 | |||
| 117 | /** |
||
| 118 | * @param TableFieldEntity $field |
||
| 119 | * @param array|null $rowData |
||
| 120 | * |
||
| 121 | * @return mixed |
||
| 122 | */ |
||
| 123 | private function getInputValue(TableFieldEntity $field, array|null $rowData): mixed |
||
| 124 | { |
||
| 125 | $update = $this->operation === 'update'; |
||
| 126 | // $default = $options["set"][$this->driver->bracketEscape($name)] ?? null; |
||
| 127 | /*if ($default === null)*/ { |
||
| 128 | $default = $field->default; |
||
| 129 | if ($field->type == "bit" && preg_match("~^b'([01]*)'\$~", $default, $regs)) { |
||
| 130 | $default = $regs[1]; |
||
| 131 | } |
||
| 132 | if ($this->driver->jush() == "sql" && preg_match('~binary~', $field->type)) { |
||
| 133 | $default = bin2hex($default); // same as UNHEX |
||
| 134 | } |
||
| 135 | } |
||
| 136 | |||
| 137 | if ($rowData === null) { |
||
|
0 ignored issues
–
show
|
|||
| 138 | return match(true) { |
||
| 139 | !$update && $field->autoIncrement => '', |
||
| 140 | $this->action === 'select' => false, |
||
| 141 | default => $default, |
||
| 142 | }; |
||
| 143 | } |
||
| 144 | |||
| 145 | $fieldValue = $rowData[$field->name] ?? null; |
||
| 146 | return match(true) { |
||
| 147 | $fieldValue !== '' && $this->driver->jush() === 'sql' && |
||
| 148 | preg_match("~enum|set~", $field->type) && |
||
| 149 | is_array($fieldValue) => implode(",", $fieldValue), |
||
| 150 | is_bool($fieldValue) => +$fieldValue, |
||
| 151 | default => $fieldValue, |
||
| 152 | }; |
||
| 153 | } |
||
| 154 | |||
| 155 | /** |
||
| 156 | * @param TableFieldEntity $field |
||
| 157 | * @param mixed $value |
||
| 158 | * |
||
| 159 | * @return array |
||
| 160 | */ |
||
| 161 | private function getInputFunction(TableFieldEntity $field, mixed $value): array |
||
| 162 | { |
||
| 163 | $formInput = []; // No user input available here. |
||
| 164 | $update = $this->operation === 'update'; |
||
| 165 | $function = match(true) { |
||
| 166 | $this->action === 'save' => $formInput['function'][$field->name] ?? '', |
||
| 167 | $update && preg_match('~^CURRENT_TIMESTAMP~i', $field->onUpdate) => 'now', |
||
| 168 | $value === false => null, |
||
| 169 | $value !== null => '', |
||
| 170 | default => 'NULL', |
||
| 171 | }; |
||
| 172 | if ($this->action !== 'save' && !$update && $value === $field->default && |
||
| 173 | preg_match('~^[\w.]+\(~', $value ?? '')) { |
||
| 174 | $function = 'SQL'; |
||
| 175 | } |
||
| 176 | if (preg_match('~time~', $field->type) && |
||
| 177 | preg_match('~^CURRENT_TIMESTAMP~i', $value ?? '')) { |
||
| 178 | $value = ""; |
||
| 179 | $function = "now"; |
||
| 180 | } |
||
| 181 | if ($field->type === "uuid" && $value === "uuid()") { |
||
| 182 | $value = ""; |
||
| 183 | $function = "uuid"; |
||
| 184 | } |
||
| 185 | |||
| 186 | return [$value, $function]; |
||
| 187 | } |
||
| 188 | |||
| 189 | /** |
||
| 190 | * Get data for an input field |
||
| 191 | * |
||
| 192 | * @param TableFieldEntity $field |
||
| 193 | * @param array|null $rowData |
||
| 194 | * |
||
| 195 | * @return FieldEditEntity |
||
| 196 | */ |
||
| 197 | public function getFieldInputValues(TableFieldEntity $field, array|null $rowData): FieldEditEntity |
||
| 198 | { |
||
| 199 | $editField = new FieldEditEntity($field); |
||
| 200 | |||
| 201 | // From html.inc.php: function edit_form(string $table, array $fields, $row, ?bool $update, string $error = '') |
||
| 202 | $value = $this->getInputValue($field, $rowData); |
||
| 203 | // if (!$this->action !== 'save' && is_string($value)) { |
||
| 204 | // $value = adminer()->editVal($value, $field); |
||
| 205 | // } |
||
| 206 | [$editField->value, $editField->function] = $this->getInputFunction($field, $value); |
||
| 207 | |||
| 208 | // From html.inc.php: input(array $field, $value, ?string $function, ?bool $autofocus = false) |
||
| 209 | $editField->name = $this->utils->html($this->driver->bracketEscape($field->name)); |
||
| 210 | $editField->fullType = $this->utils->html($field->fullType); |
||
| 211 | |||
| 212 | if (is_array($editField->value) && !$editField->function) { |
||
| 213 | // 128 - JSON_PRETTY_PRINT, 64 - JSON_UNESCAPED_SLASHES, 256 - JSON_UNESCAPED_UNICODE available since PHP 5.4 |
||
| 214 | $editField->value = json_encode($editField->value, |
||
| 215 | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); |
||
| 216 | $editField->function = 'json'; |
||
| 217 | } |
||
| 218 | |||
| 219 | // Since mssql is not yet supported, $reset is always false. |
||
| 220 | // $reset = $this->driver->jush() === 'mssql' && $field->autoIncrement; |
||
| 221 | // if ($reset && $this->action !== 'save') { |
||
| 222 | // $editField->function = null; |
||
| 223 | // } |
||
| 224 | |||
| 225 | // $editField->functions = []; |
||
| 226 | // if ($reset) { |
||
| 227 | // $editField->functions['orig'] = $this->utils->trans->lang('original'); |
||
| 228 | // } |
||
| 229 | // $editField->functions = [...$editField->functions, ...$this->editFunctions($field)]; |
||
| 230 | $editField->functions = $this->editFunctions($field); |
||
| 231 | |||
| 232 | $userType = $this->userTypes[$field->type] ?? null; |
||
| 233 | $editField->enums = $userType?->enums ?? []; |
||
| 234 | if ($editField->enums) { |
||
| 235 | $editField->type = 'enum'; |
||
| 236 | $editField->field->length = $editField->enumsLength(); |
||
| 237 | } |
||
| 238 | |||
| 239 | // Todo: process the output of tis function, which is available on MySQL only. |
||
| 240 | // echo driver()->unconvertFunction($field) . " "; |
||
| 241 | |||
| 242 | return $editField; |
||
| 243 | } |
||
| 244 | } |
||
| 245 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)or! empty(...)instead.