Skip to content
Merged
9 changes: 4 additions & 5 deletions bin/release
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ use Composer\Semver\VersionParser;
use Tempest\Console\Console;
use Tempest\Console\ConsoleApplication;
use Tempest\Console\Exceptions\InterruptException;
use Tempest\Http\Response;
use Tempest\HttpClient\HttpClient;
use Tempest\Support\Json;
use Tempest\Container;

use function Tempest\get;
use function Tempest\Support\arr;
use function Tempest\Support\str;

Expand Down Expand Up @@ -178,7 +177,7 @@ function updateBranchProtection(bool $enabled): void
$token = Tempest\env('RELEASE_GITHUB_TOKEN');
$uri = "https://api.github.com/repos/tempestphp/tempest-framework/rulesets/{$ruleset}";

$httpClient = Tempest\get(HttpClient::class);
$httpClient = Container\get(HttpClient::class);
$response = $httpClient->put(
uri: $uri,
headers: ['Authorization' => "Bearer {$token}"],
Expand All @@ -196,7 +195,7 @@ function triggerSubsplit(): void
$token = Tempest\env('RELEASE_GITHUB_TOKEN');
$uri = 'https://api.github.com/repos/tempestphp/tempest-framework/actions/workflows/subsplit-packages.yml/dispatches';

$httpClient = Tempest\get(HttpClient::class);
$httpClient = Container\get(HttpClient::class);

$response = $httpClient->post(
uri: $uri,
Expand Down Expand Up @@ -346,7 +345,7 @@ try {

performPreReleaseChecks('origin', 'main');

$console = get(Console::class);
$console = Container\get(Console::class);
$console->writeln();
$console->info(sprintf('Current version is <em>%s</em>.', $current = getCurrentVersion()));

Expand Down
4 changes: 2 additions & 2 deletions docs/1-essentials/05-container.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ The `{php}\Tempest\invoke()` function serves the same purpose when the container

### Locating a dependency

There are situations where it may not be possible to inject a dependency on a constructor. To work around this, Tempest provides the `{php}\Tempest\get()` function, which can resolve an object from the container.
There are situations where it may not be possible to inject a dependency on a constructor. To work around this, Tempest provides the `{php}\Tempest\Container\get()` function, which can resolve an object from the container.

```php
use function Tempest\get;
use function Tempest\Container\get;

$config = get(AppConfig::class);
```
Expand Down
2 changes: 1 addition & 1 deletion packages/clock/src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use Tempest\DateTime\DateTimeInterface;

use function Tempest\get;
use function Tempest\Container\get;

/**
* Get the current date and time as a {@see \Tempest\DateTime\DateTimeInterface} object.
Expand Down
21 changes: 11 additions & 10 deletions packages/command-bus/src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

declare(strict_types=1);

namespace Tempest {
use Tempest\CommandBus\CommandBus;
namespace Tempest\CommandBus;

/**
* Dispatches the given `$command` to the {@see CommandBus}, triggering all associated command handlers.
*/
function command(object $command): void
{
$commandBus = get(CommandBus::class);
use Tempest\CommandBus\CommandBus;
use Tempest\Container;

$commandBus->dispatch($command);
}
/**
* Dispatches the given `$command` to the {@see CommandBus}, triggering all associated command handlers.
*/
function command(object $command): void
{
$commandBus = Container\get(CommandBus::class);

$commandBus->dispatch($command);
}
2 changes: 1 addition & 1 deletion packages/console/src/Scheduler/GenericScheduler.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use Tempest\Process\ProcessExecutor;
use Tempest\Support\Filesystem;

use function Tempest\event;
use function Tempest\EventBus\event;
use function Tempest\internal_storage_path;

final readonly class GenericScheduler implements Scheduler
Expand Down
68 changes: 34 additions & 34 deletions packages/container/src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,42 @@

declare(strict_types=1);

namespace Tempest {
use Tempest\Container\GenericContainer;
use Tempest\Reflection\FunctionReflector;
use Tempest\Reflection\MethodReflector;
namespace Tempest\Container;

/**
* Retrieves an instance of the specified `$className` from the container.
*
* @template TClassName of object
* @param class-string<TClassName> $className
* @return TClassName
*/
function get(string $className, ?string $tag = null, mixed ...$params): object
{
$container = GenericContainer::instance();
use Tempest\Container\GenericContainer;
use Tempest\Reflection\FunctionReflector;
use Tempest\Reflection\MethodReflector;

return $container->get($className, $tag, ...$params);
}
/**
* Retrieves an instance of the specified `$className` from the container.
*
* @template TClassName of object
* @param class-string<TClassName> $className
* @return TClassName
*/
function get(string $className, ?string $tag = null, mixed ...$params): object
{
$container = GenericContainer::instance();

/**
* Invokes the given method, function, callable or invokable class from the container. If no named parameters are specified, they will be resolved from the container.
*
* #### Examples
* ```php
* \Tempest\invoke(function (MyService $service) {
* $service->execute();
* });
* ```
* ```php
* \Tempest\invoke(MyService::class, key: $apiKey);
* ```
*/
function invoke(MethodReflector|FunctionReflector|callable|string $callable, mixed ...$params): mixed
{
$container = GenericContainer::instance();
return $container->get($className, $tag, ...$params);
}

/**
* Invokes the given method, function, callable or invokable class from the container. If no named parameters are specified, they will be resolved from the container.
*
* #### Examples
* ```php
* \Tempest\invoke(function (MyService $service) {
* $service->execute();
* });
* ```
* ```php
* \Tempest\invoke(MyService::class, key: $apiKey);
* ```
*/
function invoke(MethodReflector|FunctionReflector|callable|string $callable, mixed ...$params): mixed
{
$container = GenericContainer::instance();

return $container->invoke($callable, ...$params);
}
return $container->invoke($callable, ...$params);
}
5 changes: 3 additions & 2 deletions packages/container/tests/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use PHPUnit\Framework\TestCase;
use ReflectionClass;
use Tempest\Container;
use Tempest\Container\Exceptions\CircularDependencyEncountered;
use Tempest\Container\Exceptions\DecoratorDidNotImplementInterface;
use Tempest\Container\Exceptions\DependencyCouldNotBeAutowired;
Expand Down Expand Up @@ -65,7 +66,7 @@
use Tempest\Container\Tests\Fixtures\UnionTypesClass;
use Tempest\Reflection\ClassReflector;

use function Tempest\reflect;
use function Tempest\Reflection\reflect;

/**
* @internal
Expand Down Expand Up @@ -411,7 +412,7 @@ public function test_invoke_closure_with_function(): void
GenericContainer::setInstance($container = new GenericContainer());
$container->singleton(SingletonClass::class, fn () => new SingletonClass());

$result = \Tempest\invoke(fn (SingletonClass $class) => $class::class);
$result = Container\invoke(fn (SingletonClass $class) => $class::class);

$this->assertEquals(SingletonClass::class, $result);
}
Expand Down
175 changes: 88 additions & 87 deletions packages/core/src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,104 +2,105 @@

declare(strict_types=1);

namespace Tempest {
use Closure;
use Stringable;
use Tempest\Core\Composer;
use Tempest\Core\DeferredTasks;
use Tempest\Core\EnvironmentVariableValidationFailed;
use Tempest\Core\Kernel;
use Tempest\Intl\Translator;
use Tempest\Support\Namespace\PathCouldNotBeMappedToNamespace;
use Tempest\Validation\Rule;
use Tempest\Validation\Validator;
namespace Tempest;

use function Tempest\Support\Namespace\to_psr4_namespace;
use function Tempest\Support\Path\to_absolute_path;
use Closure;
use Stringable;
use Tempest\Container;
use Tempest\Core\Composer;
use Tempest\Core\DeferredTasks;
use Tempest\Core\EnvironmentVariableValidationFailed;
use Tempest\Core\Kernel;
use Tempest\Intl\Translator;
use Tempest\Support\Namespace\PathCouldNotBeMappedToNamespace;
use Tempest\Validation\Rule;
use Tempest\Validation\Validator;

/**
* Creates an absolute path scoped to the root of the project.
*/
function root_path(Stringable|string ...$parts): string
{
return to_absolute_path(get(Kernel::class)->root, ...$parts);
}
use function Tempest\Support\Namespace\to_psr4_namespace;
use function Tempest\Support\Path\to_absolute_path;

/**
* Creates an absolute path scoped to the main directory of the project.
*/
function src_path(Stringable|string ...$parts): string
{
return root_path(get(Composer::class)->mainNamespace->path, ...$parts);
}
/**
* Creates an absolute path scoped to the root of the project.
*/
function root_path(Stringable|string ...$parts): string
{
return to_absolute_path(Container\get(Kernel::class)->root, ...$parts);
}

/**
* Creates an absolute path scoped to the framework's internal storage directory.
*/
function internal_storage_path(Stringable|string ...$parts): string
{
return to_absolute_path(get(Kernel::class)->internalStorage, ...$parts);
}
/**
* Creates an absolute path scoped to the main directory of the project.
*/
function src_path(Stringable|string ...$parts): string
{
return root_path(Container\get(Composer::class)->mainNamespace->path, ...$parts);
}

/**
* Converts the given path to a registered namespace. The path is expected to be absolute, or relative to the root of the project.
*
* @throws PathCouldNotBeMappedToNamespace If the path cannot be mapped to registered namespace
*/
function registered_namespace(Stringable|string ...$parts): string
{
return to_psr4_namespace(get(Composer::class)->namespaces, root_path(...$parts), root: root_path());
}
/**
* Creates an absolute path scoped to the framework's internal storage directory.
*/
function internal_storage_path(Stringable|string ...$parts): string
{
return to_absolute_path(Container\get(Kernel::class)->internalStorage, ...$parts);
}

/**
* Converts the given path to the main namespace. The path is expected to be absolute, or relative to the root of the project.
*
* @throws PathCouldNotBeMappedToNamespace If the path cannot be mapped to the main namespace
*/
function src_namespace(Stringable|string ...$parts): string
{
return to_psr4_namespace(get(Composer::class)->mainNamespace, root_path(...$parts), root: root_path());
}
/**
* Converts the given path to a registered namespace. The path is expected to be absolute, or relative to the root of the project.
*
* @throws PathCouldNotBeMappedToNamespace If the path cannot be mapped to registered namespace
*/
function registered_namespace(Stringable|string ...$parts): string
{
return to_psr4_namespace(Container\get(Composer::class)->namespaces, root_path(...$parts), root: root_path());
}

/**
* Retrieves the given `$key` from the environment variables. If `$key` is not defined, `$default` is returned instead.
*
* @param Rule[] $rules Optional validation rules for the value of this environment variable. If one of the rules don't pass, an exception is thrown, preventing the application from booting.
*/
function env(string $key, mixed $default = null, array $rules = []): mixed
{
$value = getenv($key);
$value = match (is_string($value) ? mb_strtolower($value) : $value) {
'true' => true,
'false' => false,
false, 'null', '' => $default,
default => $value,
};
/**
* Converts the given path to the main namespace. The path is expected to be absolute, or relative to the root of the project.
*
* @throws PathCouldNotBeMappedToNamespace If the path cannot be mapped to the main namespace
*/
function src_namespace(Stringable|string ...$parts): string
{
return to_psr4_namespace(Container\get(Composer::class)->mainNamespace, root_path(...$parts), root: root_path());
}

if ($rules === [] || ! class_exists(Validator::class) || ! interface_exists(Translator::class)) {
return $value;
}
/**
* Retrieves the given `$key` from the environment variables. If `$key` is not defined, `$default` is returned instead.
*
* @param Rule[] $rules Optional validation rules for the value of this environment variable. If one of the rules don't pass, an exception is thrown, preventing the application from booting.
*/
function env(string $key, mixed $default = null, array $rules = []): mixed
{
$value = getenv($key);
$value = match (is_string($value) ? mb_strtolower($value) : $value) {
'true' => true,
'false' => false,
false, 'null', '' => $default,
default => $value,
};

$validator = get(Validator::class);
$failures = $validator->validateValue($value, $rules);
if ($rules === [] || ! class_exists(Validator::class) || ! interface_exists(Translator::class)) {
return $value;
}

if ($failures === []) {
return $value;
}
$validator = Container\get(Validator::class);
$failures = $validator->validateValue($value, $rules);

throw new EnvironmentVariableValidationFailed(
name: $key,
value: $value,
failingRules: $failures,
validator: $validator,
);
if ($failures === []) {
return $value;
}

/**
* Defer a task, will be run after a request has been sent or a command has executed
*/
function defer(Closure $closure): void
{
get(DeferredTasks::class)->add($closure);
}
throw new EnvironmentVariableValidationFailed(
name: $key,
value: $value,
failingRules: $failures,
validator: $validator,
);
}

/**
* Defer a task, will be run after a request has been sent or a command has executed
*/
function defer(Closure $closure): void
{
Container\get(DeferredTasks::class)->add($closure);
}
Loading