Skip to content

Commit

Permalink
feature #433 Add odbc_connection_string_*() functions (derrabus)
Browse files Browse the repository at this point in the history
This PR was merged into the 1.28-dev branch.

Discussion
----------

Add odbc_connection_string_*() functions

This PR adds ODBC functions added in php/php-src#8307.

Commits
-------

1ec3314 Add odbc_connection_string_*() functions
  • Loading branch information
nicolas-grekas committed Jul 10, 2023
2 parents 05dfdff + 1ec3314 commit 544aefe
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
coverage: "none"
extensions: "apcu, intl, mbstring, uuid"
extensions: "apcu, intl, mbstring, odbc, uuid"
ini-values: "memory_limit=-1, session.gc_probability=0, apc.enable_cli=1"
php-version: "${{ matrix.php }}"
tools: "composer:v2"
Expand Down
74 changes: 74 additions & 0 deletions src/Php82/Php82.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Polyfill\Php82;

/**
* @author Alexander M. Turek <me@derrabus.de>
*
* @internal
*/
class Php82
{
/**
* Determines if a string matches the ODBC quoting rules.
*
* A valid quoted string begins with a '{', ends with a '}', and has no '}'
* inside of the string that aren't repeated (as to be escaped).
*
* These rules are what .NET also follows.
*
* @see https://github.com/php/php-src/blob/838f6bffff6363a204a2597cbfbaad1d7ee3f2b6/main/php_odbc_utils.c#L31-L57
*/
public static function odbc_connection_string_is_quoted(string $str): bool
{
if ('' === $str || '{' !== $str[0]) {
return false;
}

/* Check for } that aren't doubled up or at the end of the string */
$length = \strlen($str) - 1;
for ($i = 0; $i < $length; ++$i) {
if ('}' !== $str[$i]) {
continue;
}

if ('}' !== $str[++$i]) {
return $i === $length;
}
}

return true;
}

/**
* Determines if a value for a connection string should be quoted.
*
* The ODBC specification mentions:
* "Because of connection string and initialization file grammar, keywords and
* attribute values that contain the characters []{}(),;?*=!@ not enclosed
* with braces should be avoided."
*
* Note that it assumes that the string is *not* already quoted. You should
* check beforehand.
*
* @see https://github.com/php/php-src/blob/838f6bffff6363a204a2597cbfbaad1d7ee3f2b6/main/php_odbc_utils.c#L59-L73
*/
public static function odbc_connection_string_should_quote(string $str): bool
{
return false !== strpbrk($str, '[]{}(),;?*=!@');
}

public static function odbc_connection_string_quote(string $str): string
{
return '{'.str_replace('}', '}}', $str).'}';
}
}
3 changes: 3 additions & 0 deletions src/Php82/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ This component provides features added to PHP 8.2 core:
- [`Random\Engine`](https://wiki.php.net/rfc/rng_extension)
- [`Random\Engine\CryptoSafeEngine`](https://wiki.php.net/rfc/rng_extension)
- [`Random\Engine\Secure`](https://wiki.php.net/rfc/rng_extension) (check [arokettu/random-polyfill](https://packagist.org/packages/arokettu/random-polyfill) for more engines)
- [`odbc_connection_string_is_quoted()`](https://php.net/odbc_connection_string_is_quoted)
- [`odbc_connection_string_should_quote()`](https://php.net/odbc_connection_string_should_quote)
- [`odbc_connection_string_quote()`](https://php.net/odbc_connection_string_quote)

More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
Expand Down
18 changes: 18 additions & 0 deletions src/Php82/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@
* file that was distributed with this source code.
*/

use Symfony\Polyfill\Php82 as p;

if (\PHP_VERSION_ID >= 80200) {
return;
}

if (!extension_loaded('odbc')) {
return;
}

if (!function_exists('odbc_connection_string_is_quoted')) {
function odbc_connection_string_is_quoted(string $str): bool { return p\Php82::odbc_connection_string_is_quoted($str); }
}

if (!function_exists('odbc_connection_string_should_quote')) {
function odbc_connection_string_should_quote(string $str): bool { return p\Php82::odbc_connection_string_should_quote($str); }
}

if (!function_exists('odbc_connection_string_quote')) {
function odbc_connection_string_quote(string $str): string { return p\Php82::odbc_connection_string_quote($str); }
}
87 changes: 87 additions & 0 deletions tests/Php82/Php82Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Polyfill\Tests\Php82;

use PHPUnit\Framework\TestCase;

/**
* @requires extension odbc
*/
class Php82Test extends TestCase
{
/**
* @dataProvider provideConnectionStringValuesFromUpstream
* @dataProvider provideMoreConnectionStringValues
*/
public function testConnectionStringIsQuoted(string $value, bool $isQuoted)
{
self::assertSame($isQuoted, odbc_connection_string_is_quoted($value));
}

/**
* @dataProvider provideConnectionStringValuesFromUpstream
* @dataProvider provideMoreConnectionStringValues
*/
public function testConnectionStringShouldQuote(string $value, bool $isQuoted, bool $shouldQuote)
{
self::assertSame($shouldQuote, odbc_connection_string_should_quote($value));
}

/**
* @dataProvider provideConnectionStringValuesFromUpstream
* @dataProvider provideMoreConnectionStringValues
*/
public function testConnectionStringQuote(string $value, bool $isQuoted, bool $shouldQuote, string $quoted)
{
self::assertSame($quoted, odbc_connection_string_quote($value));
}

/**
* Test cases ported from upstream.
*
* @see https://github.com/php/php-src/blob/838f6bffff6363a204a2597cbfbaad1d7ee3f2b6/ext/odbc/tests/odbc_utils.phpt
*
* @return \Generator<string, array{string, bool, bool, string}>
*/
public static function provideConnectionStringValuesFromUpstream(): \Generator
{
// 1. No, it's not quoted.
// 2. Yes, it should be quoted because of the special character in the middle.
yield 'with_end_curly1' => ['foo}bar', false, true, '{foo}}bar}'];

// 1. No, the unescaped special character in the middle breaks what would be quoted.
// 2. Yes, it should be quoted because of the special character in the middle.
// Note that should_quote doesn't care about if the string is already quoted.
// That's why you should check if it is quoted first.
yield 'with_end_curly2' => ['{foo}bar}', false, true, '{{foo}}bar}}}'];

// 1. Yes, the special characters are escaped, so it's quoted.
// 2. See $with_end_curly2; should_quote doesn't care about if the string is already quoted.
yield 'with_end_curly3' => ['{foo}}bar}', true, true, '{{foo}}}}bar}}}'];

// 1. No, it's not quoted.
// 2. It doesn't need to be quoted because of no s
yield 'with_no_end_curly1' => ['foobar', false, false, '{foobar}'];

// 1. Yes, it is quoted and any characters are properly escaped.
// 2. See $with_end_curly2.
yield 'with_no_end_curly2' => ['{foobar}', true, true, '{{foobar}}}'];
}

/**
* @return \Generator<string, array{string, bool, bool, string}>
*/
public static function provideMoreConnectionStringValues(): \Generator
{
yield 'double curly at the end' => ['foo}}', false, true, '{foo}}}}}'];
}
}

0 comments on commit 544aefe

Please sign in to comment.