Skip to content

Commit

Permalink
Merge pull request #5 from zoilomora/encode-to-utf8
Browse files Browse the repository at this point in the history
Encode to UTF-8
  • Loading branch information
zoilomora authored Nov 20, 2020
2 parents 72ffb71 + 2826123 commit 3007bf6
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 7 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,22 @@ The default character encoding in Access databases is [Windows-1252](https://en.
If you want to convert the data to UTF-8, a simple solution would be:
```php
$field = mb_convert_encoding($field, 'UTF-8', 'Windows-1252');
$field = \mb_convert_encoding($field, 'UTF-8', 'Windows-1252');
```
If you want all the data to be encoded automatically to UTF-8 (with the performance reduction that it may imply)
configure the driver as follows:
```php
$connection = \Doctrine\DBAL\DriverManager::getConnection(
[
'driverClass' => \ZoiloMora\Doctrine\DBAL\Driver\MicrosoftAccess\Driver::class,
'dsn' => 'name of the created dsn',
'driverOptions' => [
'charset' => 'UTF-8',
],
]
);
```
## License
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"license": "MIT",
"require": {
"php": "^7.4",
"ext-mbstring": "*",
"ext-pdo": "*",
"ext-odbc": "*",
"doctrine/dbal": "^2.12"
Expand Down
8 changes: 6 additions & 2 deletions src/Doctrine/DBAL/Driver/MicrosoftAccess/PDO/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace ZoiloMora\Doctrine\DBAL\Driver\MicrosoftAccess\PDO;

use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection;
use Doctrine\DBAL\Driver\PDO\SQLSrv\Statement;
use ZoiloMora\Doctrine\DBAL\Driver\MicrosoftAccess\Statement;

final class Connection extends PDOConnection
{
Expand All @@ -18,7 +18,11 @@ public function __construct($dsn, $user = null, $password = null, $options = nul
\PDO::ATTR_STATEMENT_CLASS,
[
Statement::class,
[],
[
true === \array_key_exists('charset', $options)
? $options['charset']
: null
],
],
);
}
Expand Down
120 changes: 120 additions & 0 deletions src/Doctrine/DBAL/Driver/MicrosoftAccess/Statement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);

namespace ZoiloMora\Doctrine\DBAL\Driver\MicrosoftAccess;

use Doctrine\DBAL\ParameterType;

final class Statement extends \Doctrine\DBAL\Driver\PDO\Statement
{
private const FROM_ENCODING = 'Windows-1252';

private ?string $charset;

protected function __construct(?string $charset = null)
{
$this->charset = $charset;
}

public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null)
{
switch ($type) {
case ParameterType::LARGE_OBJECT:
case ParameterType::BINARY:
if ($driverOptions === null) {
$driverOptions = \PDO::SQLSRV_ENCODING_BINARY;
}

break;

case ParameterType::ASCII:
$type = ParameterType::STRING;
$length = 0;
$driverOptions = \PDO::SQLSRV_ENCODING_SYSTEM;
break;
}

return parent::bindParam($param, $variable, $type, $length, $driverOptions);
}

public function bindValue($param, $value, $type = ParameterType::STRING)
{
return $this->bindParam($param, $value, $type);
}

public function fetchOne()
{
return $this->convertStringEncoding(
parent::fetchOne()
);
}

public function fetchNumeric()
{
return $this->convertArrayEncoding(
parent::fetchNumeric()
);
}

public function fetchAssociative()
{
return $this->convertArrayEncoding(
parent::fetchAssociative()
);
}

public function fetchAllNumeric(): array
{
return $this->convertCollectionEncoding(
parent::fetchAllNumeric()
);
}

public function fetchFirstColumn(): array
{
return $this->convertArrayEncoding(
parent::fetchFirstColumn()
);
}

public function fetchAllAssociative(): array
{
return $this->convertCollectionEncoding(
parent::fetchAllAssociative()
);
}

private function convertCollectionEncoding(array $items): array
{
\array_walk(
$items,
function (&$item) {
$item = $this->convertArrayEncoding($item);
}
);

return $items;
}

private function convertArrayEncoding(array $items): array
{
foreach ($items as $key => $value) {
$items[$key] = $this->convertStringEncoding($items[$key]);
}

return $items;
}

private function convertStringEncoding(?string $value): ?string
{
if (null === $this->charset) {
return $value;
}

if (null === $value) {
return null;
}

return \mb_convert_encoding($value, $this->charset, self::FROM_ENCODING);
}
}
28 changes: 24 additions & 4 deletions tests/BaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@

abstract class BaseTest extends TestCase
{
protected function filename(): string
{
return 'default.mdb';
}

protected function dsn(): string
{
return 'dbal-msaccess';
}

protected function driverOptions(): array
{
return [];
}

public function connections(): \Iterator
{
if (false === $this->isMicrosoftWindows()) {
Expand All @@ -19,7 +34,8 @@ public function connections(): \Iterator

$params = [
'driverClass' => Driver::class,
'dsn' => 'dbal-msaccess',
'dsn' => $this->dsn(),
'driverOptions' => $this->driverOptions(),
];

$connection = DriverManager::getConnection($params);
Expand All @@ -28,7 +44,11 @@ public function connections(): \Iterator
$connection->connect();
} catch (\Doctrine\DBAL\Exception $exception) {
throw new \Exception(
"You should create a DSN pointing to 'var\database.mdb' called 'dbal-msaccess'",
\sprintf(
"You should create a DSN pointing to 'var\%s' called '%s'",
$this->filename(),
$this->dsn()
),
);
}

Expand All @@ -45,8 +65,8 @@ private function createDatabase(): string
$ds = \DIRECTORY_SEPARATOR;
$root = __DIR__ . $ds . '..' . $ds;

$source = $root . 'tests' . $ds . 'database.mdb';
$dest = $root . 'var' . $ds . 'database.mdb';
$source = $root . 'tests' . $ds . 'Databases' . $ds . $this->filename();
$dest = $root . 'var' . $ds . $this->filename();

\copy($source, $dest);

Expand Down
Binary file added tests/Databases/Iurie-popovt-test.accdb
Binary file not shown.
Binary file renamed tests/database.mdb → tests/Databases/default.mdb
Binary file not shown.
46 changes: 46 additions & 0 deletions tests/Doctrine/DBAL/Driver/MicrosoftAccess/StatementTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);

namespace ZoiloMora\Tests\Doctrine\DBAL\Driver\MicrosoftAccess;

use Doctrine\DBAL\Connection;
use ZoiloMora\Tests\BaseTest;

class StatementTest extends BaseTest
{
private const TABLE_NAME = 'test';

protected function filename(): string
{
return 'Iurie-popovt-test.accdb';
}

protected function dsn(): string
{
return 'Iurie-popovt-test';
}

protected function driverOptions(): array
{
return [
'charset' => 'UTF-8',
];
}

/**
* @test
* @dataProvider connections
*/
public function given_a_french_database_when_has_driver_options_then_it_appears_in_utf8(Connection $connection)
{
$result = $connection
->createQueryBuilder()
->select('note')
->from(self::TABLE_NAME)
->execute();

$item = $result->fetchAllAssociative()[0];

$this->assertSame('Matériel Prêt un mail est envoyé', $item['note']);
}
}

0 comments on commit 3007bf6

Please sign in to comment.