Skip to content

Commit 544aefe

Browse files
feature #433 Add odbc_connection_string_*() functions (derrabus)
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
2 parents 05dfdff + 1ec3314 commit 544aefe

File tree

5 files changed

+183
-1
lines changed

5 files changed

+183
-1
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
uses: shivammathur/setup-php@v2
3737
with:
3838
coverage: "none"
39-
extensions: "apcu, intl, mbstring, uuid"
39+
extensions: "apcu, intl, mbstring, odbc, uuid"
4040
ini-values: "memory_limit=-1, session.gc_probability=0, apc.enable_cli=1"
4141
php-version: "${{ matrix.php }}"
4242
tools: "composer:v2"

src/Php82/Php82.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Polyfill\Php82;
13+
14+
/**
15+
* @author Alexander M. Turek <[email protected]>
16+
*
17+
* @internal
18+
*/
19+
class Php82
20+
{
21+
/**
22+
* Determines if a string matches the ODBC quoting rules.
23+
*
24+
* A valid quoted string begins with a '{', ends with a '}', and has no '}'
25+
* inside of the string that aren't repeated (as to be escaped).
26+
*
27+
* These rules are what .NET also follows.
28+
*
29+
* @see https://github.com/php/php-src/blob/838f6bffff6363a204a2597cbfbaad1d7ee3f2b6/main/php_odbc_utils.c#L31-L57
30+
*/
31+
public static function odbc_connection_string_is_quoted(string $str): bool
32+
{
33+
if ('' === $str || '{' !== $str[0]) {
34+
return false;
35+
}
36+
37+
/* Check for } that aren't doubled up or at the end of the string */
38+
$length = \strlen($str) - 1;
39+
for ($i = 0; $i < $length; ++$i) {
40+
if ('}' !== $str[$i]) {
41+
continue;
42+
}
43+
44+
if ('}' !== $str[++$i]) {
45+
return $i === $length;
46+
}
47+
}
48+
49+
return true;
50+
}
51+
52+
/**
53+
* Determines if a value for a connection string should be quoted.
54+
*
55+
* The ODBC specification mentions:
56+
* "Because of connection string and initialization file grammar, keywords and
57+
* attribute values that contain the characters []{}(),;?*=!@ not enclosed
58+
* with braces should be avoided."
59+
*
60+
* Note that it assumes that the string is *not* already quoted. You should
61+
* check beforehand.
62+
*
63+
* @see https://github.com/php/php-src/blob/838f6bffff6363a204a2597cbfbaad1d7ee3f2b6/main/php_odbc_utils.c#L59-L73
64+
*/
65+
public static function odbc_connection_string_should_quote(string $str): bool
66+
{
67+
return false !== strpbrk($str, '[]{}(),;?*=!@');
68+
}
69+
70+
public static function odbc_connection_string_quote(string $str): string
71+
{
72+
return '{'.str_replace('}', '}}', $str).'}';
73+
}
74+
}

src/Php82/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ This component provides features added to PHP 8.2 core:
99
- [`Random\Engine`](https://wiki.php.net/rfc/rng_extension)
1010
- [`Random\Engine\CryptoSafeEngine`](https://wiki.php.net/rfc/rng_extension)
1111
- [`Random\Engine\Secure`](https://wiki.php.net/rfc/rng_extension) (check [arokettu/random-polyfill](https://packagist.org/packages/arokettu/random-polyfill) for more engines)
12+
- [`odbc_connection_string_is_quoted()`](https://php.net/odbc_connection_string_is_quoted)
13+
- [`odbc_connection_string_should_quote()`](https://php.net/odbc_connection_string_should_quote)
14+
- [`odbc_connection_string_quote()`](https://php.net/odbc_connection_string_quote)
1215

1316
More information can be found in the
1417
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).

src/Php82/bootstrap.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,24 @@
99
* file that was distributed with this source code.
1010
*/
1111

12+
use Symfony\Polyfill\Php82 as p;
13+
1214
if (\PHP_VERSION_ID >= 80200) {
1315
return;
1416
}
17+
18+
if (!extension_loaded('odbc')) {
19+
return;
20+
}
21+
22+
if (!function_exists('odbc_connection_string_is_quoted')) {
23+
function odbc_connection_string_is_quoted(string $str): bool { return p\Php82::odbc_connection_string_is_quoted($str); }
24+
}
25+
26+
if (!function_exists('odbc_connection_string_should_quote')) {
27+
function odbc_connection_string_should_quote(string $str): bool { return p\Php82::odbc_connection_string_should_quote($str); }
28+
}
29+
30+
if (!function_exists('odbc_connection_string_quote')) {
31+
function odbc_connection_string_quote(string $str): string { return p\Php82::odbc_connection_string_quote($str); }
32+
}

tests/Php82/Php82Test.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Polyfill\Tests\Php82;
13+
14+
use PHPUnit\Framework\TestCase;
15+
16+
/**
17+
* @requires extension odbc
18+
*/
19+
class Php82Test extends TestCase
20+
{
21+
/**
22+
* @dataProvider provideConnectionStringValuesFromUpstream
23+
* @dataProvider provideMoreConnectionStringValues
24+
*/
25+
public function testConnectionStringIsQuoted(string $value, bool $isQuoted)
26+
{
27+
self::assertSame($isQuoted, odbc_connection_string_is_quoted($value));
28+
}
29+
30+
/**
31+
* @dataProvider provideConnectionStringValuesFromUpstream
32+
* @dataProvider provideMoreConnectionStringValues
33+
*/
34+
public function testConnectionStringShouldQuote(string $value, bool $isQuoted, bool $shouldQuote)
35+
{
36+
self::assertSame($shouldQuote, odbc_connection_string_should_quote($value));
37+
}
38+
39+
/**
40+
* @dataProvider provideConnectionStringValuesFromUpstream
41+
* @dataProvider provideMoreConnectionStringValues
42+
*/
43+
public function testConnectionStringQuote(string $value, bool $isQuoted, bool $shouldQuote, string $quoted)
44+
{
45+
self::assertSame($quoted, odbc_connection_string_quote($value));
46+
}
47+
48+
/**
49+
* Test cases ported from upstream.
50+
*
51+
* @see https://github.com/php/php-src/blob/838f6bffff6363a204a2597cbfbaad1d7ee3f2b6/ext/odbc/tests/odbc_utils.phpt
52+
*
53+
* @return \Generator<string, array{string, bool, bool, string}>
54+
*/
55+
public static function provideConnectionStringValuesFromUpstream(): \Generator
56+
{
57+
// 1. No, it's not quoted.
58+
// 2. Yes, it should be quoted because of the special character in the middle.
59+
yield 'with_end_curly1' => ['foo}bar', false, true, '{foo}}bar}'];
60+
61+
// 1. No, the unescaped special character in the middle breaks what would be quoted.
62+
// 2. Yes, it should be quoted because of the special character in the middle.
63+
// Note that should_quote doesn't care about if the string is already quoted.
64+
// That's why you should check if it is quoted first.
65+
yield 'with_end_curly2' => ['{foo}bar}', false, true, '{{foo}}bar}}}'];
66+
67+
// 1. Yes, the special characters are escaped, so it's quoted.
68+
// 2. See $with_end_curly2; should_quote doesn't care about if the string is already quoted.
69+
yield 'with_end_curly3' => ['{foo}}bar}', true, true, '{{foo}}}}bar}}}'];
70+
71+
// 1. No, it's not quoted.
72+
// 2. It doesn't need to be quoted because of no s
73+
yield 'with_no_end_curly1' => ['foobar', false, false, '{foobar}'];
74+
75+
// 1. Yes, it is quoted and any characters are properly escaped.
76+
// 2. See $with_end_curly2.
77+
yield 'with_no_end_curly2' => ['{foobar}', true, true, '{{foobar}}}'];
78+
}
79+
80+
/**
81+
* @return \Generator<string, array{string, bool, bool, string}>
82+
*/
83+
public static function provideMoreConnectionStringValues(): \Generator
84+
{
85+
yield 'double curly at the end' => ['foo}}', false, true, '{foo}}}}}'];
86+
}
87+
}

0 commit comments

Comments
 (0)