Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added CredentialProvider #308

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
367cc4e
Selection: support for generics
dg May 10, 2024
0608c21
PascalCase constants
dg May 10, 2024
90bb859
Reflection::getTable() can access unlisted table
dg May 13, 2024
a96116c
renamed IStructure::FIELD_* to Type::*
dg May 10, 2024
ebbb7cd
Driver::getColumns() added NDB type
dg May 13, 2024
da1b2c8
Column::$nativeType changed to NDB $type
dg May 10, 2024
d43272b
Helpers::detectType() supports 'INT UNSIGNED'
dg May 10, 2024
9e87eda
Reflection: removed $schema
dg May 13, 2024
e11f612
opened 4.0-dev
dg Mar 1, 2021
c4e5746
removed old Driver::SUPPORT constants (BC break)
dg May 10, 2024
57e5fa0
deprecated methods trigger notices
dg Jan 19, 2022
ccd13ce
returns always date-time as immutable Nette\Database\DateTime (BC break)
dg May 10, 2024
b23a3b3
Helpers::normalizeRow() & detection moved to new class RowNormalizer
dg May 13, 2024
3e7168b
RowNormalizer: refactoring
dg Dec 14, 2023
229fec5
RowNormalizer: added configuring methods [Closes #257]
dg Jan 19, 2022
98d167f
introduced PdoDriver, descendant of all PDO-based drivers
dg Sep 20, 2021
1297897
database connection moved to PdoDriver::connect()
dg Dec 3, 2023
38b88d5
drivers uses PDO instead of Connection
dg Nov 30, 2023
ccece35
some others methods moved to PdoDriver
dg Dec 13, 2023
c7c81ee
added ResultDriver
dg May 10, 2024
2cf6efa
exceptions are converted in PdoDriver
dg Sep 20, 2021
071d442
added Nette\Database\QueryException
dg Sep 20, 2021
860a033
drivers: getForeignKeys() works with multi-column foreign keys
dg Jan 19, 2022
c328e2c
readme: added jumbo
dg May 16, 2024
9bf1d51
added CredentialProvider
Jun 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
"dev-master": "4.0-dev"
}
}
}
22 changes: 14 additions & 8 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
Nette Database
==============
[![Nette Database](https://github.com/nette/database/assets/194960/97d8f31b-096c-466c-a76f-f5b9e511ea8d)](https://doc.nette.org/database)

[![Downloads this Month](https://img.shields.io/packagist/dm/nette/database.svg)](https://packagist.org/packages/nette/database)
[![Tests](https://github.com/nette/database/actions/workflows/tests.yml/badge.svg?branch=v3.2)](https://github.com/nette/database/actions)
[![Tests](https://github.com/nette/database/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/nette/database/actions)
[![Latest Stable Version](https://poser.pugx.org/nette/database/v/stable)](https://github.com/nette/database/releases)
[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/database/blob/master/license.md)

 <!---->

Introduction
------------

Nette provides a powerful layer for accessing your database easily.

- composes SQL queries with ease
- easily fetches data
- uses efficient queries and does not transmit unnecessary data
composes SQL queries with ease<br>
✅ significantly simplifies retrieving data without writing SQL queries<br>
uses efficient queries and does not transmit unnecessary data

The [Nette Database Core](https://doc.nette.org/database-core) is a wrapper around the PDO and provides core functionality.
The [Nette Database Core](https://doc.nette.org/en/database/core) is a wrapper around the PDO and provides core functionality.

The [Nette Database Explorer](https://doc.nette.org/database-explorer) layer helps you to fetch database data more easily and in a more optimized way.
The [Nette Database Explorer](https://doc.nette.org/en/database/explorer) layer helps you to fetch database data more easily and in a more optimized way.

 <!---->

[Support Me](https://github.com/sponsors/dg)
--------------------------------------------
Expand All @@ -30,6 +31,7 @@ Do you like Nette Database? Are you looking forward to the new features?

Thank you!

 <!---->

Installation
------------
Expand All @@ -42,12 +44,14 @@ composer require nette/database

It requires PHP version 8.1 and supports PHP up to 8.3.

 <!---->

Usage
-----

This is just a piece of documentation. [Please see our website](https://doc.nette.org/database).

 <!---->

Database Core
-------------
Expand All @@ -71,6 +75,8 @@ $database->query('UPDATE users SET ? WHERE id=?', $data, $id);
$database->query('SELECT * FROM categories WHERE id=?', 123)->dump();
```

 <!---->

Database Explorer
-----------------

Expand Down
5 changes: 4 additions & 1 deletion src/Bridges/DatabaseDI/DatabaseExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ public function getConfigSchema(): Nette\Schema\Schema
Expect::structure([
'dsn' => Expect::string()->required()->dynamic(),
'user' => Expect::string()->nullable()->dynamic(),
'password' => Expect::string()->nullable()->dynamic(),
'password' => Expect::anyOf(
Expect::string()->nullable()->dynamic(),
Expect::type(Nette\Database\CredentialProvider::class)->nullable(),
),
'options' => Expect::array(),
'debugger' => Expect::bool(),
'explain' => Expect::bool(true),
Expand Down
64 changes: 29 additions & 35 deletions src/Database/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@

use JetBrains\PhpStorm\Language;
use Nette\Utils\Arrays;
use PDO;
use PDOException;


/**
Expand All @@ -25,12 +23,11 @@ class Connection

/** @var array<callable(self, ResultSet|DriverException): void> Occurs after query is executed */
public array $onQuery = [];
private Driver $driver;
private ?Driver $driver = null;
private SqlPreprocessor $preprocessor;
private ?PDO $pdo = null;

/** @var callable(array, ResultSet): array */
private $rowNormalizer = [Helpers::class, 'normalizeRow'];
private $rowNormalizer;
private ?string $sql = null;
private int $transactionDepth = 0;

Expand All @@ -40,12 +37,11 @@ public function __construct(
#[\SensitiveParameter]
private readonly ?string $user = null,
#[\SensitiveParameter]
private readonly ?string $password = null,
private readonly string|CredentialProvider|null $password = null,
private readonly array $options = [],
) {
if (!empty($options['newDateTime'])) {
$this->rowNormalizer = fn($row, $resultSet) => Helpers::normalizeRow($row, $resultSet, DateTime::class);
}
$this->rowNormalizer = new RowNormalizer;

if (empty($options['lazy'])) {
$this->connect();
}
Expand All @@ -54,23 +50,25 @@ public function __construct(

public function connect(): void
{
if ($this->pdo) {
if ($this->driver) {
return;
}

try {
$this->pdo = new PDO($this->dsn, $this->user, $this->password, $this->options);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
throw ConnectionException::from($e);
}

$dsn = explode(':', $this->dsn)[0];
$class = empty($this->options['driverClass'])
? 'Nette\Database\Drivers\\' . ucfirst(str_replace('sql', 'Sql', $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME))) . 'Driver'
? 'Nette\Database\Drivers\\' . ucfirst(str_replace('sql', 'Sql', $dsn)) . 'Driver'
: $this->options['driverClass'];
if (!class_exists($class)) {
throw new ConnectionException("Invalid data source '$dsn'.");
}

$password = $this->password instanceof CredentialProvider
? $this->password->getPassword($this)
: $this->password;

$this->driver = new $class;
$this->driver->connect($this->dsn, $this->user, $password, $this->options);
$this->preprocessor = new SqlPreprocessor($this);
$this->driver->initialize($this, $this->options);
Arrays::invoke($this->onConnect, $this);
}

Expand All @@ -84,7 +82,7 @@ public function reconnect(): void

public function disconnect(): void
{
$this->pdo = null;
$this->driver = null;
}


Expand All @@ -94,10 +92,11 @@ public function getDsn(): string
}


public function getPdo(): PDO
/** deprecated use getDriver()->getPdo() */
public function getPdo(): \PDO
{
$this->connect();
return $this->pdo;
return $this->driver->getPdo();
}


Expand All @@ -111,6 +110,7 @@ public function getDriver(): Driver
/** @deprecated use getDriver() */
public function getSupplementalDriver(): Driver
{
trigger_error(__METHOD__ . '() is deprecated, use getDriver()', E_USER_DEPRECATED);
$this->connect();
return $this->driver;
}
Expand All @@ -131,22 +131,15 @@ public function setRowNormalizer(?callable $normalizer): static

public function getInsertId(?string $sequence = null): string
{
try {
$res = $this->getPdo()->lastInsertId($sequence);
return $res === false ? '0' : $res;
} catch (PDOException $e) {
throw $this->driver->convertException($e);
}
$this->connect();
return $this->driver->getInsertId($sequence);
}


public function quote(string $string, int $type = PDO::PARAM_STR): string
public function quote(string $string): string
{
try {
return $this->getPdo()->quote($string, $type);
} catch (PDOException $e) {
throw DriverException::from($e);
}
$this->connect();
return $this->driver->quote($string);
}


Expand Down Expand Up @@ -216,7 +209,7 @@ public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')]
[$this->sql, $params] = $this->preprocess($sql, ...$params);
try {
$result = new ResultSet($this, $this->sql, $params, $this->rowNormalizer);
} catch (PDOException $e) {
} catch (DriverException $e) {
Arrays::invoke($this->onQuery, $this, $e);
throw $e;
}
Expand All @@ -229,6 +222,7 @@ public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')]
/** @deprecated use query() */
public function queryArgs(string $sql, array $params): ResultSet
{
trigger_error(__METHOD__ . '() is deprecated, use query()', E_USER_DEPRECATED);
return $this->query($sql, ...$params);
}

Expand Down
22 changes: 22 additions & 0 deletions src/Database/CredentialProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/

declare(strict_types=1);

namespace Nette\Database;


/**
* Allows to use dynamic password (token authorizations).
*/
interface CredentialProvider
{
/**
* Returns currently valid password for initialization of connection
*/
function getPassword(): string;
}
44 changes: 26 additions & 18 deletions src/Database/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,44 @@


/**
* Supplemental PDO database driver.
* Supplemental database driver.
*/
interface Driver
{
public const
SUPPORT_SEQUENCE = 'sequence',
SUPPORT_SELECT_UNGROUPED_COLUMNS = 'ungrouped_cols',
SUPPORT_MULTI_INSERT_AS_SELECT = 'insert_as_select',
SUPPORT_MULTI_COLUMN_AS_OR_COND = 'multi_column_as_or',
SUPPORT_SUBSELECT = 'subselect',
SUPPORT_SCHEMA = 'schema';
SupportSequence = 'sequence',
SupportSelectUngroupedColumns = 'ungrouped_cols',
SupportMultiInsertAsSelect = 'insert_as_select',
SupportMultiColumnAsOrCond = 'multi_column_as_or',
SupportSubselect = 'subselect',
SupportSchema = 'schema';

/**
* Initializes connection.
* @throws ConnectionException
*/
function initialize(Connection $connection, array $options): void;
function connect(string $dsn, ?string $user = null, ?string $password = null, ?array $options = null): void;

function query(string $queryString, array $params): ResultDriver;

function beginTransaction(): void;

function commit(): void;

function rollBack(): void;

/**
* Returns the ID of the last inserted row or sequence value.
*/
function getInsertId(?string $sequence = null): string;

/**
* Converts PDOException to DriverException or its descendant.
* Delimits string for use in SQL statement.
*/
function convertException(\PDOException $e): DriverException;
function quote(string $string): string;

/**
* Delimites identifier for use in a SQL statement.
* Delimits identifier for use in SQL statement.
*/
function delimite(string $name): string;

Expand Down Expand Up @@ -72,15 +86,9 @@ function getIndexes(string $table): array;
/** @return list<array{name: string, local: string, table: string, foreign: string}> */
function getForeignKeys(string $table): array;

/**
* Returns associative array of detected types (IStructure::FIELD_*) in result set.
* @return array<string, string>
*/
function getColumnTypes(\PDOStatement $statement): array;

/**
* Cheks if driver supports specific property
* @param string $item self::SUPPORT_* property
* @param self::Support* $item
*/
function isSupported(string $item): bool;
}
Expand Down
Loading