Skip to content

Commit

Permalink
Allow options to be provided to Doctrine
Browse files Browse the repository at this point in the history
Signed-off-by: Andrew Summers <[email protected]>

Fix issues with return type for `OC_App::findAppInDirectories`
  • Loading branch information
summersab committed Sep 18, 2023
1 parent e0c778f commit afa42fd
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 6 deletions.
28 changes: 28 additions & 0 deletions config/config.sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -1824,6 +1824,34 @@
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET wait_timeout = 28800'
],

/**
* Allows defining custom DB types and options as well as the ability to override
* the default connection parameters as defined by
* OC\DB\ConnectionFactory::defaultConnectionParams.
*/
'dbconnectionparams' => [
'dbtype' => [
'adapter' => AdapterMySQL::class,
'charset' => 'UTF8',
'driver' => 'pdo_dbtype',
'wrapperClass' => Doctrine\DBAL\Connection::class,
],
],

/**
* Allows additional configuration options to be provided for the database
* connection as defined by Doctrine\DBAL\Configuration::class. This is useful
* for specifying a custom logger, middlewares, etc.
*/
'dbconfigurationparams' => [
"sqllogger" => 'Doctrine\DBAL\Logging\SQLLogger',
"resultcache" => 'Psr\Cache\CacheItemPoolInterface',
"schemaassetsfilter" => 'callable',
"autocommit" => true,
"middlewares" => array(
'Doctrine\DBAL\Driver\Middleware',
),
],
/**
* sqlite3 journal mode can be specified using this configuration parameter -
* can be 'WAL' or 'DELETE' see for more details https://www.sqlite.org/wal.html
Expand Down
51 changes: 47 additions & 4 deletions lib/private/AppFramework/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,15 @@
use OC\AppFramework\DependencyInjection\DIContainer;
use OC\AppFramework\Http\Dispatcher;
use OC\AppFramework\Http\Request;
use OCP\App\IAppManager;
use OCP\Profiler\IProfiler;
use OC\Profiler\RoutingDataCollector;
use OCP\AppFramework\QueryException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\ICallbackResponse;
use OCP\AppFramework\Http\IOutput;
use OCP\AppFramework\QueryException;
use OCP\Diagnostics\IEventLogger;
use OCP\HintException;
use OCP\IRequest;
use OCP\Profiler\IProfiler;

/**
* Entry point for every request in your app. You can consider this as your
Expand All @@ -69,7 +68,7 @@ public static function buildAppNamespace(string $appId, string $topNamespace = '
return $topNamespace . self::$nameSpaceCache[$appId];
}

$appInfo = \OCP\Server::get(IAppManager::class)->getAppInfo($appId);
$appInfo = self::getAppInfo($appId);
if (isset($appInfo['namespace'])) {
self::$nameSpaceCache[$appId] = trim($appInfo['namespace']);
} else {
Expand All @@ -91,11 +90,42 @@ public static function buildAppNamespace(string $appId, string $topNamespace = '
return $topNamespace . self::$nameSpaceCache[$appId];
}

public static function getAppInfo(string $appId, bool $path = false, $lang = null) {
if ($path) {
$file = $appId;
} else {
$dir = \OC_App::findAppInDirectories($appId);

if ($dir === false) {
return null;
}

$appPath = $dir['path'] . '/' . $appId;
$file = $appPath . '/appinfo/info.xml';
}

$parser = new \OC\App\InfoParser();
$data = $parser->parse($file);

if (is_array($data)) {
$data = \OC_App::parseAppInfo($data, $lang);
}

return $data;
}

public static function getAppIdForClass(string $className, string $topNamespace = 'OCA\\'): ?string {
if (!str_starts_with($className, $topNamespace)) {
return null;
}

if (str_starts_with($className, $topNamespace)) {
$classNoTopNamespace = substr($className, strlen($topNamespace));
$appNameParts = explode('\\', $classNoTopNamespace, 2);
$appName = reset($appNameParts);
return strtolower($appName);
}

foreach (self::$nameSpaceCache as $appId => $namespace) {
if (str_starts_with($className, $topNamespace . $namespace . '\\')) {
return $appId;
Expand All @@ -105,6 +135,19 @@ public static function getAppIdForClass(string $className, string $topNamespace
return null;
}

public static function registerAppClass(string $className): void {
$classParts = explode('\\', $className, 2);
$topNamespace = reset($classParts) . '\\';
$appId = self::getAppIdForClass($className, $topNamespace);
$dir = \OC_App::findAppInDirectories($appId);

if ($dir === false) {
throw new \OCP\App\AppPathNotFoundException('App not found in any app directory');
}

$appPath = $dir['path'] . '/' . $appId;
\OC_App::registerAutoloading($appId, $appPath);
}

/**
* Shortcut for calling a controller method and printing the result
Expand Down
49 changes: 48 additions & 1 deletion lib/private/DB/ConnectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Event\Listeners\OracleSessionInit;
use Doctrine\DBAL\Event\Listeners\SQLSessionInit;
use OC\AppFramework\App;
use OC\SystemConfig;

/**
Expand Down Expand Up @@ -93,6 +94,21 @@ public function __construct(SystemConfig $systemConfig) {
if ($collationOverride) {
$this->defaultConnectionParams['mysql']['collation'] = $collationOverride;
}

$connectionParams = $this->config->getValue('dbconnectionparams', array()) ?: array();

foreach ($connectionParams as $type) {
foreach ($type as $key => $param) {
switch ($key) {
case 'adapter':
case 'wrapperClass':
\OC::$server->get(App::class)::registerAppClass($param);
break;
}
}
}

$this->defaultConnectionParams = array_replace_recursive($this->defaultConnectionParams, $connectionParams);
}

/**
Expand Down Expand Up @@ -158,9 +174,40 @@ public function getConnection($type, $additionalConnectionParams) {
break;
}
/** @var Connection $connection */
$configuration = new Configuration();

foreach ($this->config->getValue('dbconfigurationparams', array()) as $param => $value) {
switch ($param) {
case "sqllogger":
\OC::$server->get(App::class)::registerAppClass($value);
$configuration->setSQLLogger(new $value());

Check failure

Code scanning / Psalm

TaintedCallable Error

Detected tainted text

Check failure

Code scanning / Psalm

TaintedCallable Error

Detected tainted text
break;
case "resultcache":
\OC::$server->get(App::class)::registerAppClass($value);
$configuration->setResultCache(new $value());

Check failure

Code scanning / Psalm

TaintedCallable Error

Detected tainted text

Check failure

Code scanning / Psalm

TaintedCallable Error

Detected tainted text
break;
case "schemaassetsfilter":
$configuration->setSchemaAssetsFilter($value);
break;
case "autocommit":
$configuration->setAutoCommit($value);
break;
case "middlewares":
$middlewares = array();

foreach ($value as $middleware) {
\OC::$server->get(App::class)::registerAppClass($middleware);
array_push($middlewares, new $middleware());

Check failure

Code scanning / Psalm

TaintedCallable Error

Detected tainted text
}

$configuration->setMiddlewares($value);
break;
}
}

$connection = DriverManager::getConnection(
array_merge($this->getDefaultConnectionParams($type), $additionalConnectionParams),
new Configuration(),
$configuration,
$eventManager
);
return $connection;

Check failure on line 213 in lib/private/DB/ConnectionFactory.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

LessSpecificReturnStatement

lib/private/DB/ConnectionFactory.php:213:10: LessSpecificReturnStatement: The type 'Doctrine\DBAL\Connection' is more general than the declared return type 'OC\DB\Connection' for OC\DB\ConnectionFactory::getConnection (see https://psalm.dev/129)
Expand Down
2 changes: 1 addition & 1 deletion lib/private/legacy/OC_App.php
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ public static function getInstallPath() {
*
* @param string $appId
* @param bool $ignoreCache ignore cache and rebuild it
* @return false|string
* @return false|array
*/
public static function findAppInDirectories(string $appId, bool $ignoreCache = false) {
$sanitizedAppId = self::cleanAppId($appId);
Expand Down

0 comments on commit afa42fd

Please sign in to comment.