cloudbear/api-oci

Build OCI image archives and interact with OCI registries from PHP.

Maintainers

Package info

gitlab.com/cloudbear/open-source/php-api-oci

Issues

pkg:composer/cloudbear/api-oci

Transparency log

Statistics

Installs: 75

Dependents: 0

Suggesters: 0

Stars: 0

v2.1.0 2026-07-30 16:11 UTC

This package is auto-updated.

Last update: 2026-07-31 06:06:17 UTC


README

A PHP library for building OCI archives and interacting with OCI registries — without a container build daemon.

  • Build OCI archives with multi-architecture and multi-layer support (no build system required).
  • Push archives to, and read manifests/tags from, OCI registries.
  • Produces standard .tar / .tar.gz archives that docker load and other OCI tools understand.

Requirements

  • PHP 8.4 or newer
  • ext-zlib (used to gzip archives; archives are assembled entirely in memory)

Installation

composer require cloudbear/api-oci

Building an archive

use Cloudbear\Oci\Container;
use Cloudbear\Oci\Container\Config;
use Cloudbear\Oci\Container\Enums\ChangeTypes;
use Cloudbear\Oci\Container\Files\ContentFile;
use Cloudbear\Oci\Container\Layer;
use Cloudbear\Oci\Enums\PlatformVariants;
use Cloudbear\Oci\Name;

// Single file
$archive = Container::fromFile(
    name: Name::parse('gitlab.com/cloudbear/open-source/php-api-oci/example:latest'),
    file: new ContentFile(ChangeTypes::Addition, '/readme.txt', 'My archive'),
);

// Multi-architecture, multi-layer
$archive = Container::fromConfig(
    Name::parse('gitlab.com/cloudbear/open-source/php-api-oci/example:latest'),
    new Config(PlatformVariants::ARM_64BIT, layers: [
        new Layer([
            new ContentFile(ChangeTypes::Addition, '/readme.txt', "I'm ARM"),
        ]),
    ]),
    new Config(PlatformVariants::X86_64, layers: [
        new Layer([
            new ContentFile(ChangeTypes::Addition, '/readme.txt', "I'm X86_64"),
        ]),
    ]),
);

// Write a local archive (compression is inferred from the extension)
$archive->writeToDisk('my-archive.tar.gz');

Talking to a registry

Registry is the entry point for all registry access: reading tags and manifests, pushing an archive, and pulling one into a Container. It is built on a RegistryTransport, which owns the HTTP client and handles token authentication.

use Cloudbear\Oci\Registry;
use Cloudbear\Oci\Registry\Credentials;
use Cloudbear\Oci\Registry\RegistryTransport;

// Credentials are optional; omit them for anonymous access.
$registry = new Registry(new RegistryTransport(new Credentials($username, $password)));

// Read tags and manifests.
$tags = $registry->tags('ghcr.io/owner/app');
$manifest = $registry->detail('ghcr.io/owner/app:v1');

// Push a locally built archive.
$registry->push($archive);

Pulling from a registry

Registry::pull() returns a Container. The pulled layers keep their exact blob bytes and digests, so the container can be re-pushed byte-identically, and their files can be read in memory.

use Cloudbear\Oci\Registry;
use Cloudbear\Oci\Registry\RegistryTransport;

$container = new Registry()->pull('ghcr.io/owner/app:v1');

$container->paths();                 // file paths in the latest (topmost) layer
$container->has('app/config.yaml');  // check for a path
$container->get('app/config.yaml');  // read a file's contents, or null if absent
$container->files();                 // ['path' => 'contents', ...] for the latest layer
$container->latestLayer();           // the Layer itself, or null when there are none

Local Docker daemon

DockerDaemon talks to a local Docker daemon over its unix socket (no docker CLI, no temp files). It can load a built Container into the daemon (docker load), export an image back out into a Container (docker save), and list or inspect images.

use Cloudbear\Oci\DockerDaemon;

// Defaults to /var/run/docker.sock; honors a unix:// DOCKER_HOST, or pass a socket path.
$daemon = new DockerDaemon();

// Load a locally built archive into the daemon.
$daemon->load($archive);

// Export an image out of the daemon; exported layers keep their exact digests.
$container = $daemon->export('ghcr.io/owner/app:v1');

$daemon->images();                          // GET /images/json  (list)
$daemon->inspect('ghcr.io/owner/app:v1');   // GET /images/{ref}/json  (inspect)

The whole export is read into memory, so this suits small-to-moderate images; streaming large exports to disk is not yet supported. Only a unix-socket daemon is supported (not TCP/TLS DOCKER_HOST or containerd).

Names

Name::parse() accepts the usual reference forms and fills in Docker Hub defaults:

Name::parse('nginx');                                // registry-1.docker.io/library/nginx:latest
Name::parse('nginx:1.27');                           // ... /library/nginx:1.27
Name::parse('ghcr.io/owner/app:v1');                 // ghcr.io/owner/app:v1
Name::parse('registry.example.com/app@sha256:...');  // pinned by digest

Development

composer install
composer test           # PHPUnit
composer test:coverage  # PHPUnit with a coverage report, needs pcov or Xdebug
composer phpstan        # PHPStan, level 8
composer lint           # PHP-CS-Fixer, writes fixes in place
composer check          # test, phpstan and lint, in order

See CONTRIBUTING.md for the full workflow.

Contributing

This library is partially implemented and contributions are welcome. Please read CONTRIBUTING.md and our Code of Conduct before opening a merge request. For security issues, see SECURITY.md.

License

Released under the MIT License.