Skip to content

Commit aaf8fed

Browse files
committed
Merge branch '0.4.x'
# Conflicts: # composer.json # src/Bootstrap/Config.php # src/Db/Adapter/Pdo/Mysql.php # src/Locale.php # src/Mvc/Controller/Traits/Params.php # src/Mvc/Controller/Traits/Query.php # src/Mvc/Model/Behavior/Conditional.php # src/Support/Env.php # src/Support/HelperFactory.php # src/Support/Options/Options.php
2 parents a51a537 + d80c0a2 commit aaf8fed

File tree

19 files changed

+579
-25
lines changed

19 files changed

+579
-25
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"ext-sodium": "*",
6565
"docopt/docopt": "^1.0.5",
6666
"league/flysystem": "^3.29.1",
67-
"league/fractal": "^0.20.1",
67+
"league/fractal": "^0.20.2",
6868
"phalcon/devtools": "~5.0",
6969
"phalcon/incubator-mailer": "^2.0",
7070
"vlucas/phpdotenv": "^5.6.1"

src/Bootstrap.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class Bootstrap
8585
/**
8686
* @throws Exception
8787
*/
88-
public function __construct(string $mode = null)
88+
public function __construct(?string $mode = null)
8989
{
9090
$this->setMode($mode);
9191
$this->setEventsManager(new Events\Manager());
@@ -229,7 +229,7 @@ public function bootServices(): void
229229
/**
230230
* Register modules
231231
*/
232-
public function registerModules(AbstractApplication $application = null, ?array $modules = null, ?string $defaultModule = null): void
232+
public function registerModules(?AbstractApplication $application = null, ?array $modules = null, ?string $defaultModule = null): void
233233
{
234234
$application ??= $this->isMvc()
235235
? $this->di->get('application')

src/Bootstrap/Config.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,8 @@ public function __construct(array $data = [], bool $insensitive = false)
10801080
'charset' => Env::get('DATABASE_CHARSET', 'utf8'),
10811081
'options' => [
10821082
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . Env::get('DATABASE_CHARSET', 'utf8') .
1083-
', sql_mode = \'' . Env::get('DATABASE_SQL_MODE', 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION') . '\'',
1083+
', sql_mode = \'' . Env::get('DATABASE_SQL_MODE', 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION') . '\'' .
1084+
', block_encryption_mode = \''.Env::get('DATABASE_BLOCK_ENCRYPTION_MODE', 'aes-256-cbc').'\'',
10841085
\PDO::ATTR_EMULATE_PREPARES => Env::get('DATABASE_EMULATE_PREPARES', false), // https://stackoverflow.com/questions/10113562/pdo-mysql-use-pdoattr-emulate-prepares-or-not
10851086
\PDO::ATTR_STRINGIFY_FETCHES => Env::get('DATABASE_STRINGIFY_FETCHES', false),
10861087
\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => Env::get('DATABASE_SSL_VERIFY_SERVER_CERT', true),

src/Cli/Console.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
class Console extends \Phalcon\Cli\Console
1717
{
18-
public function __construct(DiInterface $container = null)
18+
public function __construct(?DiInterface $container = null)
1919
{
2020
parent::__construct($container);
2121
}

src/Cli/Module.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class Module implements ModuleDefinitionInterface
3535
/**
3636
* Registers an autoloader related to the frontend module
3737
*/
38-
public function registerAutoloaders(DiInterface $container = null): void
38+
public function registerAutoloaders(?DiInterface $container = null): void
3939
{
4040
$this->loader = $container['loader'] ?? new Loader();
4141
assert($this->loader instanceof Loader);
@@ -90,7 +90,7 @@ public function getNamespaces(): array
9090
return $namespaces;
9191
}
9292

93-
public function getServices(DiInterface $container = null): void
93+
public function getServices(?DiInterface $container = null): void
9494
{
9595
$this->loader = $container['loader'] ?? new Loader();
9696
$this->config ??= $container['config'] ?? new Config();

src/Db/Adapter/Pdo/Mysql.php

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,117 @@
1515

1616
class Mysql extends \Phalcon\Db\Adapter\Pdo\Mysql
1717
{
18+
public function describeColumns(string $table, ?string $schema = null): array
19+
{
20+
$definitions = parent::describeColumns($table, $schema);
21+
22+
if (Column::TYPE_TINYINTEGER !== Column::TYPE_BINARY) {
23+
return $definitions;
24+
}
25+
26+
foreach ($definitions as $definitionKey => $definition) {
27+
28+
if ($definition->getType() === Column::TYPE_TINYINTEGER && !$definition->isNumeric()) {
29+
// probably a binary at this point
30+
31+
$newDefinition = [];
32+
33+
// protected to public
34+
$prefix = chr(0) . '*' . chr(0);
35+
foreach ((array)$definition as $key => $value) {
36+
$newDefinition[str_replace($prefix, '', $key)] = $value;
37+
}
38+
39+
$newDefinition['bindType'] = Column::BIND_PARAM_BLOB;
40+
$newDefinition['type'] = Column::TYPE_VARBINARY;
41+
unset($newDefinition['scale']);
42+
43+
// reset definition
44+
$definitions[$definitionKey] = new Column($definition->getName(), $newDefinition);
45+
}
46+
}
47+
48+
return $definitions;
49+
}
50+
51+
/**
52+
* Overrides the executePrepared method to rewrite duplicate placeholders.
53+
*
54+
* @param \PDOStatement $statement The original PDO statement.
55+
* @param array $placeholders An array of bind values.
56+
* @param array $dataTypes An array of bind types.
57+
*
58+
* @return \PDOStatement
59+
*/
60+
public function executePrepared(\PDOStatement $statement, array $placeholders, $dataTypes): \PDOStatement
61+
{
62+
// Get the original SQL from the statement.
63+
$sql = $statement->queryString;
64+
65+
// Rewrite the SQL to ensure unique parameter names and update the placeholders.
66+
[$newSql, $newPlaceholders, $newDataTypes] = $this->rewriteQueryPlaceholders($sql, $placeholders, $dataTypes);
67+
68+
// Prepare a new statement with the rewritten SQL.
69+
$newStatement = $this->pdo->prepare($newSql);
70+
71+
// Call parent's executePrepared with the new statement.
72+
return parent::executePrepared($newStatement, $newPlaceholders, $newDataTypes);
73+
}
74+
75+
/**
76+
* Rewrites an SQL query by replacing duplicate named placeholders with unique ones.
77+
*
78+
* This function scans the SQL for named placeholders (like :paramName) and for every
79+
* duplicate occurrence (beyond the first), it appends a unique suffix (e.g. :paramName_2)
80+
* and duplicates the corresponding bind value and type.
81+
*
82+
* It also handles cases where the bind arrays use keys without a leading colon.
83+
*
84+
* @param string $sql The original SQL query.
85+
* @param array $bind The bind values array.
86+
* @param array $bindTypes The bind types array.
87+
*
88+
* @return array An array containing the new SQL, the new bind values, and the new bind types.
89+
*/
90+
public function rewriteQueryPlaceholders(string $sql, array $bind, array $bindTypes): array
91+
{
92+
// Pattern to match named placeholders, e.g. ":paramName"
93+
$pattern = '/(:[a-zA-Z0-9_]+)/';
94+
$placeholderCount = [];
95+
96+
$newSql = preg_replace_callback($pattern, function ($matches) use (&$placeholderCount, &$bind, &$bindTypes) {
97+
$placeholder = $matches[1]; // e.g. ":myParam"
98+
99+
// Determine the key used in the bind arrays.
100+
// Some arrays use keys with the colon, others without.
101+
$originalKey = array_key_exists($placeholder, $bind)
102+
? $placeholder
103+
: ltrim($placeholder, ':');
104+
105+
// If this is the first occurrence, leave it as is.
106+
if (!isset($placeholderCount[$placeholder])) {
107+
$placeholderCount[$placeholder] = 1;
108+
return $placeholder;
109+
}
110+
111+
// For subsequent occurrences, increment the counter and generate a unique placeholder.
112+
$placeholderCount[$placeholder]++;
113+
$newPlaceholder = $placeholder . '_' . $placeholderCount[$placeholder];
114+
115+
// Adjust the new key to match the original style.
116+
$newKey = (str_starts_with($originalKey, ':')) ? $newPlaceholder : ltrim($newPlaceholder, ':');
117+
118+
// Duplicate the bind value and type for the new placeholder.
119+
if (isset($bind[$originalKey])) {
120+
$bind[$newKey] = $bind[$originalKey];
121+
}
122+
if (isset($bindTypes[$originalKey])) {
123+
$bindTypes[$newKey] = $bindTypes[$originalKey];
124+
}
125+
126+
return $newPlaceholder;
127+
}, $sql);
128+
129+
return [$newSql, $bind, $bindTypes];
130+
}
18131
}

src/Identity/Traits/Role.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ trait Role
2929
public function hasRole(?array $roles = null, bool $or = false, bool $inherit = true): bool
3030
{
3131
$roleList = array_keys($this->getRoleList());
32-
return $this->has($roles, $inherit ? $this->getInheritedRoleList($roleList) : $roleList, $or);
32+
return $this->has($roles, $inherit ? array_merge($roleList, $this->getInheritedRoleList($roleList)) : $roleList, $or);
3333
}
3434

3535
/**

src/Locale.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ public function getFromHttp(?string $default = null): ?string
246246
/**
247247
* Save locale into session if mode contain session handling
248248
*/
249-
public function saveIntoSession(?string $locale = null, bool $force = false): void
249+
public function saveIntoSession(?string $locale = null, ?bool $force = false): void
250250
{
251251
$locale ??= $this->getLocale();
252252

0 commit comments

Comments
 (0)