Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
93 changes: 93 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1078,3 +1078,96 @@ These methods now safely accept **a single argument**, defaulting the missing pa
- Prevents PHP warnings in production environments.

This bug fix enhances robustness and backward compatibility for developers using dynamic number formatting features in MagicObject.


# MagicObject Version 3.14.5

## What's Changed

### New Feature: URL-Based Database Credential Parsing

MagicObject now supports importing database credentials from a **datasource URL** string using the new method `PicoDatabaseCredentials::importFromUrl()`.

This enhancement simplifies configuration and integration with environment-based or externalized connection settings (e.g., `DATABASE_URL`).

#### Supported URL Format

driver://username:password@host:port/database?schema=public&charset=utf8&timezone=Asia/Jakarta


#### Example

```php
$credentials = new PicoDatabaseCredentials();
$credentials->importFromUrl(
'mysql://user:secret@localhost:3306/myapp?schema=public&charset=utf8mb4&timezone=Asia/Jakarta'
);
```

#### Optional Override

Username and password can be passed directly to override the values in the URL:

```php
$credentials->importFromUrl($url, 'realuser', 'realpass');
```

This is useful when credentials are stored separately from the connection string.

#### Special Handling for SQLite

When using sqlite:///path/to/database.db, the file path is automatically mapped to databaseFilePath instead of host/port.

```php
$url = 'sqlite:///path/to/database.db';
$credentials->importFromUrl($url);
```

### Why It Matters

- Makes deployment and configuration more flexible in containerized or cloud environments.

- Simplifies integration with .env files, environment variables, or external secrets managers.

- Supports both traditional and SQLite-based databases.

### Backward Compatibility

This update is fully backward-compatible and does not change any existing behavior unless the new method is used explicitly.


### Bug Fix: Class-Typed Default Parameters Now Compatible with PHP 5

Fixed a **fatal error** caused by the use of default parameters with a class type hint (`MagicObject`, `SecretObject`, `SetterGetter`, `MagicDto`) and non-null default values in the `validate()` method.

#### Before (Problematic in PHP 5):

```php
public function validate(
$parentPropertyName = null,
$messageTemplate = null,
MagicObject $reference = null,
bool $validateIfReferenceEmpty = true // ❌ Causes error in PHP 5
)
```

After (PHP 5 Compatible):

```php
public function validate(
$parentPropertyName = null,
$messageTemplate = null,
MagicObject $reference = null,
$validateIfReferenceEmpty = true // ✅ Compatible with PHP 5
)
```

> This change ensures full compatibility with legacy environments running PHP 5, while maintaining functionality in modern PHP versions.

### Backward Compatibility

- This version is **fully backward-compatible**.

- No breaking changes were introduced.

- Existing codebases will continue to function as-is unless the new functionality is explicitly invoked.
304 changes: 261 additions & 43 deletions docs/doc.html

Large diffs are not rendered by default.

83 changes: 83 additions & 0 deletions src/Database/PicoDatabaseCredentials.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace MagicObject\Database;

use InvalidArgumentException;
use MagicObject\SecretObject;

/**
Expand All @@ -17,9 +18,12 @@
* ```php
* <?php
* $credentials = new PicoDatabaseCredentials();
* $credentials->setDriver('mysql');
* $credentials->setHost('localhost');
* $credentials->setPort(3306);
* $credentials->setUsername('user');
* $credentials->setPassword('password');
* $credentials->setDatabaseName('app');
* ```
*
* The attributes are automatically encrypted when set, providing a secure way to handle sensitive
Expand Down Expand Up @@ -113,4 +117,83 @@ class PicoDatabaseCredentials extends SecretObject
*/
protected $charset;

/**
* Import database credentials from a URL datasource string.
*
* Supported format:
* scheme://username:password@host:port/database?schema=public&charset=utf8&timezone=Asia/Jakarta
*
* @param string $url The datasource URL
* @param string|null $username Optional username to override the one from URL
* @param string|null $password Optional password to override the one from URL
* @return self Returns the current instance for method chaining.
*/
public function importFromUrl($url, $username = null, $password = null) // NOSONAR
{
$parts = parse_url($url);

if (!$parts) {
throw new InvalidArgumentException("Invalid database URL");
}

// Basic connection parts
if (isset($parts['scheme'])) {
$this->setDriver($parts['scheme']);
}

if ($this->getDriver() === 'sqlite' && isset($parts['path'])) {
$this->setDatabaseFilePath($parts['path']);
return;
}

if (isset($parts['host'])) {
$this->setHost($parts['host']);
}

if (isset($parts['port'])) {
$port = (int) $parts['port'];
if ($port < 0) {
throw new InvalidArgumentException("Invalid port: must be a non-negative integer");
}
$this->setPort($port);
}

// Username and password
if ($username !== null) {
$this->setUsername($username);
} elseif (isset($parts['user'])) {
$this->setUsername($parts['user']);
}

if ($password !== null) {
$this->setPassword($password);
} elseif (isset($parts['pass'])) {
$this->setPassword($parts['pass']);
}

if (isset($parts['path'])) {
$dbName = ltrim($parts['path'], '/');
$this->setDatabaseName($dbName);
}

// Optional query parameters
if (isset($parts['query'])) {
parse_str($parts['query'], $query);

if (isset($query['schema'])) {
$this->setDatabaseSchema($query['schema']);
}

if (isset($query['timezone'])) {
$this->setTimeZone($query['timezone']);
}

if (isset($query['charset'])) {
$this->setCharset($query['charset']);
}
}
return $this;
}


}
2 changes: 1 addition & 1 deletion src/Database/PicoDatabasePersistenceExtended.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public function validate(
$parentPropertyName = null,
$messageTemplate = null,
$reference = null,
bool $validateIfReferenceEmpty = true // Changed default to true and type hint to bool
$validateIfReferenceEmpty = true // Changed default to true and type hint to bool
) {
$objectToValidate = $this->object; // Default: validate this object
$shouldValidate = true; // Flag to determine if validation should proceed
Expand Down
2 changes: 1 addition & 1 deletion src/MagicDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,7 @@ public function validate(
$parentPropertyName = null,
$messageTemplate = null,
$reference = null,
bool $validateIfReferenceEmpty = true
$validateIfReferenceEmpty = true
) {
$objectToValidate = $this; // Default: validate this object
$shouldValidate = true; // Flag to determine if validation should proceed
Expand Down
2 changes: 1 addition & 1 deletion src/MagicObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -3134,7 +3134,7 @@ public function validate(
$parentPropertyName = null,
$messageTemplate = null,
$reference = null,
bool $validateIfReferenceEmpty = true
$validateIfReferenceEmpty = true
) {
$objectToValidate = $this; // Default: validate this object
$shouldValidate = true; // Flag to determine if validation should proceed
Expand Down
2 changes: 1 addition & 1 deletion src/SecretObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -1252,7 +1252,7 @@ public function validate(
$parentPropertyName = null,
$messageTemplate = null,
$reference = null,
bool $validateIfReferenceEmpty = true
$validateIfReferenceEmpty = true
) {
$objectToValidate = $this; // Default: validate this object
$shouldValidate = true; // Flag to determine if validation should proceed
Expand Down
2 changes: 1 addition & 1 deletion src/SetterGetter.php
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ public function validate(
$parentPropertyName = null,
$messageTemplate = null,
$reference = null,
bool $validateIfReferenceEmpty = true
$validateIfReferenceEmpty = true
) {
$objectToValidate = $this; // Default: validate this object
$shouldValidate = true; // Flag to determine if validation should proceed
Expand Down