diff --git a/lib/ArangoDBClient/Database.php b/lib/ArangoDBClient/Database.php index 3ffd7cac..ac583ab6 100644 --- a/lib/ArangoDBClient/Database.php +++ b/lib/ArangoDBClient/Database.php @@ -43,7 +43,7 @@ class Database * This creates a new database
* * @param Connection $connection - the connection to be used - * @param string $name - database name, for example 'myDatabase' + * @param string $name - database name, for example 'myDatabase' - must be NFC-normalized! * @param array $options - extra options for new collections in this database. *

Options are :
*

  • 'replicationFactor'
  • @@ -58,16 +58,6 @@ class Database */ public static function create(Connection $connection, $name, array $options = []) { - try { - // NFC-normalize the database name, as this is required - // by the server - if (class_exists("\Normalizer", false)) { - $name = \Normalizer::normalize($name, \Normalizer::FORM_C); - } - } catch (\Exception $e) { - // don't fail if Unicode normalization doesn't work. - // probably it is not installed. - } $payload = [ self::ENTRY_DATABASE_NAME => $name, self::ENTRY_DATABASE_USERS => [ @@ -195,6 +185,33 @@ public static function getInfo(Connection $connection) return $response->getJson(); } + + + /** + * normalizes a database name + * + * UTF-8 NFC Normalization is required for database names in case + * the extended naming scheme for databases is used. This has to + * be enabled on the server side and is present since server version + * 3.9. + * If the name needs normalization but no normalizer is installed, + * this function can fail and abort the program with a PHP fatal error. + * + * @param string $name - database name to normalize. + * + * @return string $name - The normalized name + */ + public static function normalizeName($name) + { + // first check if the database name follows the traditional + // naming scheme. if so, there is no need to normalize it. + if (!preg_match("/^[a-zA-Z0-9_\-]+$/", $name)) { + // extended database naming scheme. now NFC-normalize + // the database name, as this is required by the server + $name = \Normalizer::normalize($name, \Normalizer::FORM_C); + } + return $name; + } } class_alias(Database::class, '\triagens\ArangoDb\Database'); diff --git a/lib/ArangoDBClient/UrlHelper.php b/lib/ArangoDBClient/UrlHelper.php index b936309d..2a37a27a 100644 --- a/lib/ArangoDBClient/UrlHelper.php +++ b/lib/ArangoDBClient/UrlHelper.php @@ -66,7 +66,7 @@ public static function buildUrl($baseUrl, array $parts = []) @list(,$part) = explode('/', $part); } - $url .= '/' . urlencode($part); + $url .= '/' . rawurlencode($part); } return $url; diff --git a/tests/DatabaseTest.php b/tests/DatabaseTest.php index d2621a07..1af71028 100644 --- a/tests/DatabaseTest.php +++ b/tests/DatabaseTest.php @@ -45,6 +45,79 @@ public function setUp(): void } } + + public function testCreateDatabaseWithUnicodeName() + { + if (!class_exists("\Normalizer", false)) { + $this->markTestSkipped("unable to find Normalizer class. maybe php-intl is not installed?"); + return; + } + + // try to create a database with Unicode name. + // this may fail if the server side is not configured to allow + // Unicode database names + $database = "tröt tröt tröt_" . static::$testsTimestamp; + try { + $response = Database::create($this->connection, $database); + } catch (ServerException $exception) { + // ERROR_ARANGO_DATABASE_NAME_INVALID,1229,"database name invalid","Will be raised when an invalid database name is used." + if ($exception->getServerCode() === 1229) { + $this->markTestSkipped("server was not started with extended database naming scheme"); + return; + } + throw $exception; + } + + $response = Database::listDatabases($this->connection); + static::assertArrayHasKey($database, array_flip($response['result'])); + } + + + public function testCreateDatabaseWithUnicodeNameNormalization() + { + if (!class_exists("\Normalizer", false)) { + $this->markTestSkipped("unable to find Normalizer class. maybe php-intl is not installed?"); + return; + } + + $databases = [ "😀", "ﻚﻠﺑ ﻞﻄﻴﻓ", "かわいい犬" ]; + + // try to create a database with Unicode name. + // this may fail if the server side is not configured to allow + // Unicode database names + foreach ($databases as $database) { + $database = Database::normalizeName($database); + + try { + Database::delete($this->connection, $database); + } catch (\Exception $ex) { + // try to get rid of existing databases first. ignore if it does not exist. + } + + try { + $response = Database::create($this->connection, $database); + } catch (ServerException $exception) { + // ERROR_ARANGO_DATABASE_NAME_INVALID,1229,"database name invalid","Will be raised when an invalid database name is used." + if ($exception->getServerCode() === 1229) { + $this->markTestSkipped("server was not started with extended database naming scheme"); + return; + } + throw $exception; + } + + try { + $response = Database::listDatabases($this->connection); + static::assertArrayHasKey($database, array_flip($response['result'])); + + Database::delete($this->connection, $database); + } catch (\Exception $ex) { + // always clean up + Database::delete($this->connection, $database); + throw $ex; + } + } + } + /** * Test if Databases can be created and deleted */ @@ -53,7 +126,6 @@ public function testCreateDatabaseDeleteIt() $database = 'ArangoTestSuiteDatabaseTest01' . '_' . static::$testsTimestamp; try { - $e = null; Database::delete($this->connection, $database); } catch (\Exception $e) { // don't bother us... just give us the $e @@ -354,7 +426,11 @@ public function tearDown(): void $this->connection->setDatabase('_system'); // clean up - $databases = ['ArangoTestSuiteDatabaseTest01' . '_' . static::$testsTimestamp, 'ArangoTestSuiteDatabaseTest02' . '_' . static::$testsTimestamp]; + $databases = [ + 'ArangoTestSuiteDatabaseTest01' . '_' . static::$testsTimestamp, + 'ArangoTestSuiteDatabaseTest02' . '_' . static::$testsTimestamp, + 'tröt tröt tröt_' . static::$testsTimestamp, + ]; foreach ($databases as $database) { try { diff --git a/tests/travis/setup_arangodb.sh b/tests/travis/setup_arangodb.sh index 33763e23..ce778715 100644 --- a/tests/travis/setup_arangodb.sh +++ b/tests/travis/setup_arangodb.sh @@ -3,12 +3,12 @@ echo "PHP version: $TRAVIS_PHP_VERSION" if [[ "$TRAVIS_PHP_VERSION" == "7.4" ]] ; then -wget "https://phar.phpunit.de/phpunit-9.5.phar" +wget --no-check-certificate "https://phar.phpunit.de/phpunit-9.5.phar" mv phpunit-9.5.phar ./phpunit fi if [[ "$TRAVIS_PHP_VERSION" == "8.0" ]] ; then -wget "https://phar.phpunit.de/phpunit-9.5.phar" +wget --no-check-certificate "https://phar.phpunit.de/phpunit-9.5.phar" mv phpunit-9.5.phar ./phpunit fi @@ -21,7 +21,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd $DIR docker pull arangodb/arangodb-preview:3.9.0-nightly -docker run -d -e ARANGO_ROOT_PASSWORD="test" -p 8529:8529 arangodb/arangodb-preview:3.9.0-nightly +docker run -d -e ARANGO_ROOT_PASSWORD="test" -p 8529:8529 arangodb/arangodb-preview:3.9.0-nightly arangod --database.extended-names-databases true sleep 2