Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/Propel/Runtime/ActiveQuery/ModelCriteria.php
Original file line number Diff line number Diff line change
Expand Up @@ -1945,25 +1945,31 @@ public function deleteAll(?ConnectionInterface $con = null): int
/**
* Code to execute before every UPDATE statement
*
* @param array $values The associative array of columns and values for the update
* $values is an associative array of columns and values for a normal
* update(), or a Criteria when update() is called with one (as the
* generated ON DELETE SET NULL emulation does). The parameter is left
* untyped so both forms reach doUpdate(), which handles each — matching the
* untyped basePreUpdate() the QueryBuilder generates for behavior hooks.
*
* @param array|\Propel\Runtime\ActiveQuery\Criteria $values The associative array of columns and values for the update
* @param \Propel\Runtime\Connection\ConnectionInterface $con The connection object used by the query
* @param bool $forceIndividualSaves If false (default), the resulting call is a Criteria::doUpdate(), otherwise it is a series of save() calls on all the found objects
*
* @return int|null
*/
protected function basePreUpdate(array &$values, ConnectionInterface $con, bool $forceIndividualSaves = false): ?int
protected function basePreUpdate(&$values, ConnectionInterface $con, bool $forceIndividualSaves = false): ?int
{
return $this->preUpdate($values, $con, $forceIndividualSaves);
}

/**
* @param array $values
* @param array|\Propel\Runtime\ActiveQuery\Criteria $values
* @param \Propel\Runtime\Connection\ConnectionInterface $con
* @param bool $forceIndividualSaves
*
* @return int|null
*/
protected function preUpdate(array &$values, ConnectionInterface $con, bool $forceIndividualSaves = false): ?int
protected function preUpdate(&$values, ConnectionInterface $con, bool $forceIndividualSaves = false): ?int
{
return null;
}
Expand Down
6 changes: 6 additions & 0 deletions src/Propel/Runtime/Adapter/Pdo/PdoAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ public function getConnection(array $params): PdoConnection
$con = new PdoConnection($dsn, $user, $password, $driverOptions);
$this->initConnection($con, isset($params['settings']) && is_array($params['settings']) ? $params['settings'] : []);
} catch (PDOException $e) {
// Detailed error logging
error_log(sprintf('PDO Connection Error: DSN: %s, Error: %s, Code: %s',
preg_replace('/password=.*?;/', 'password=***;', $dsn),
$e->getMessage(),
$e->getCode()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Password logged when last DSN segment

High Severity

The new PDO connection error logging tries to mask credentials with preg_replace('/password=.*?;/', ...), but Propel DSNs often end with ;password=... and no trailing semicolon. In that common layout the pattern never matches, so the real password is written to error_log on connection failure.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3776f37. Configure here.

));
throw new AdapterException('Unable to open PDO connection', 0, $e);
}

Expand Down
63 changes: 44 additions & 19 deletions src/Propel/Runtime/Connection/ConnectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,28 +51,53 @@ public static function create(
} else {
$connectionClass = $defaultConnectionClass;
}
try {
$adapterConnection = $adapter->getConnection($configuration);
} catch (AdapterException $e) {
throw new ConnectionException('Unable to open connection', 0, $e);
}
/** @var \Propel\Runtime\Connection\ConnectionInterface $connection */
$connection = new $connectionClass($adapterConnection);

// load any connection options from the config file
// connection attributes are those PDO flags that have to be set on the initialized connection
if (isset($configuration['attributes']) && is_array($configuration['attributes'])) {
foreach ($configuration['attributes'] as $option => $value) {
if (is_string($value) && strpos($value, '::') !== false) {
if (!defined($value)) {
throw new InvalidArgumentException(sprintf('Invalid class constant specified "%s" while processing connection attributes for datasource "%s"', $value, $connection->getName()));

$maxRetries = 2;
$retryCount = 0;
$lastException = null;

while ($retryCount <= $maxRetries) {
try {
$adapterConnection = $adapter->getConnection($configuration);

/** @var \Propel\Runtime\Connection\ConnectionInterface $connection */
$connection = new $connectionClass($adapterConnection);

// load any connection options from the config file
// connection attributes are those PDO flags that have to be set on the initialized connection
if (isset($configuration['attributes']) && is_array($configuration['attributes'])) {
foreach ($configuration['attributes'] as $option => $value) {
if (is_string($value) && strpos($value, '::') !== false) {
if (!defined($value)) {
throw new InvalidArgumentException(sprintf('Invalid class constant specified "%s" while processing connection attributes for datasource "%s"', $value, $connection->getName()));
}
$value = constant($value);
}
$connection->setAttribute($option, $value);
}
$value = constant($value);
}
$connection->setAttribute($option, $value);

return $connection;

} catch (AdapterException $e) {
$lastException = $e;
$retryCount++;

// Log the connection attempt failure
error_log(sprintf('Propel connection attempt %d/%d failed: %s',
$retryCount,
$maxRetries + 1,
$e->getMessage() . ' - ' . ($e->getPrevious() ? $e->getPrevious()->getMessage() : '')
));

if ($retryCount <= $maxRetries) {
// Wait before retrying
usleep(100000); // 100ms
}
}
}

return $connection;

// If we get here, all retries have failed
throw new ConnectionException('Unable to open connection after ' . ($maxRetries + 1) . ' attempts', 0, $lastException);
}
}