adachsoft / gitlib
Lightweight, framework-agnostic Git library with flat facade and action dispatcher for PHP.
1.0.0
2025-09-12 05:28 UTC
Requires
- php: ^8.3
- adachsoft/command-executor-lib: ^1.0
Requires (Dev)
- phpunit/phpunit: ^12.3
This package is not auto-updated.
Last update: 2025-09-13 02:04:14 UTC
README
Lightweight, framework-agnostic Git library with a flat facade and a name-based action dispatcher. Built for PHP 8.3+, PSR-compliant, easily extensible with plugin-like operation handlers.
Requirements
- PHP 8.3+
- Composer
Installation
composer require adachsoft/gitlib
Quick start
<?php
use Adachsoft\CommandExecutor\SimpleCommandExecutor;
use Adachsoft\GitLab\Internal\Adapter\CommandExecutorProcessRunner;
use Adachsoft\GitLab\Internal\Operation\HandlerLocator;
use Adachsoft\GitLab\Internal\Operation\OperationRegistry;
use Adachsoft\GitLab\Internal\GitRepository;
use Adachsoft\GitLab\DTO\Options\PullOptions;
use Adachsoft\GitLab\DTO\Options\PushOptions;
use Adachsoft\GitLab\Exception\ProcessFailedException;
use Adachsoft\GitLab\Exception\ValidationException;
$executor = new SimpleCommandExecutor();
$runner = new CommandExecutorProcessRunner($executor);
$locator = new HandlerLocator();
$registry = (new OperationRegistry())
->discoverAndRegisterHandlers($locator, $runner, __DIR__);
$repo = new GitRepository($runner, $registry, __DIR__); // working directory
try {
// 1) Using the facade methods
$status = $repo->status();
$repo->add(['file1.php', 'file2.php']);
$repo->commit('Initial commit');
$repo->pull(new PullOptions(remote: 'origin', branch: 'main', rebase: true));
$repo->push(new PushOptions(remote: 'origin', branch: 'main', forceWithLease: true));
// 2) Using the action dispatcher
$repo->execute('getRemotes', ['verbose' => true]);
$repo->execute('rebase', ['branch' => 'feature/xyz']);
$repo->execute('push', [
'remote' => 'origin',
'branch' => 'main',
'setUpstream' => true,
]);
} catch (ValidationException|ProcessFailedException $e) {
// Handle invalid arguments or failed git processes
echo $e->getMessage();
}
Concepts
- Facade:
GitRepositoryInterface
exposes a full set of explicit Git operations (no magicexecute()
here). - Dispatcher:
GitActionExecutorInterface::execute(string $action, array $args)
allows dynamic invocation by action name. - Handlers: Each action is handled by a class implementing
OperationHandlerInterface
and marked with#[GitOperation('actionName')]
. - Process execution: Abstracted via
ProcessRunnerInterface
with the default adapterCommandExecutorProcessRunner
(wrapsadachsoft/command-executor-lib
). - DTOs: Simple
final readonly
objects (no getters) for structured results.
Auto-discovery of handlers
HandlerLocator
discovers and instantiates handlers marked with #[GitOperation]
in the Adachsoft\GitLab\Extensions
namespace (filesystem defaults to src/Extensions
). You can customize the directory and namespace if needed.
$registry->discoverAndRegisterHandlers($locator, $runner, $workingDirectory);
Adding a custom action
1) Create a handler in src/Extensions
:
<?php
namespace Adachsoft\GitLab\Extensions;
use Adachsoft\GitLab\Attributes\GitOperation;
use Adachsoft\GitLab\Contracts\ProcessRunnerInterface;
use Adachsoft\GitLab\Contracts\OperationHandlerInterface;
#[GitOperation('hello')]
final class HelloHandler extends AbstractOperationHandler implements OperationHandlerInterface
{
public function name(): string { return 'hello'; }
public function handle(array $arguments)
{
$this->processRunner->run('git status', $this->workingDirectory);
return 'world';
}
}
2) Let the locator discover it (nothing else to change).
Exceptions
ValidationException
– invalid arguments/options passed to operations/handlers.ProcessFailedException
– underlying git command failed (non-zero exit, etc.).
Notes
- Framework-agnostic: no container required; wire objects manually.
- PSR style; PHPDoc/comments in English.
- See
docs/tasks/task001-plan-v1.md
for the original design plan and acceptance criteria.