sqon/sqon

Reads and writes Sqons.

0.10.0 2016-08-17 03:28 UTC

This package is auto-updated.

Last update: 2024-04-10 14:01:11 UTC


README

Build Status Coverage Status SensioLabsInsight

Sqon

Usage

The following uses a Symfony 3 project as an example. Sqon itself is project agnostic, so it will work on any PHP project regardless of how it is implemented.

use Sqon\Iterator\DirectoryIterator;
use Sqon\Path\Memory;
use Sqon\Sqon;

// Start the process of creating a new Sqon.
Sqon::create('project.sqon')

    // Enable GZIP compression.
    ->setCompression(Sqon::GZIP)

    // Add all files in a Symfony 3 project directory.
    ->setPathsUsingIterator(new DirectoryIterator('test'))

    // Execute the console when the Sqon is executed.
    ->setPath(
        Sqon::PRIMARY,
        new Memory("<?php require __DIR__ . '/../bin/console';")
    )

    // Commit the changes to a file on disk.
    ->commit()

;

When you run php project.sqon you will see the Symfony 3 console.

Requirements

  • Current PHP or HHVM long term support release.
    • pdo_sqlite
    • bz2 (if using bzip2 compression)
    • zlib (if using gzip compression)

Install

Add as a Composer dependency:

composer require sqon/sqon

Documentation

As a developer creating or reading a Sqon, you will primarily be relying on the bundled implementation of SqonInterface, Sqon. You will need to read the interface documentation to have a high level understanding of how a Sqon is created, read, and modified.

Interacting with paths (files and directories) stored in a Sqon requires an understanding of the PathInterface interface. When reading from a Sqon, you will be working with the Memory implementation of PathInterface. When adding individual paths to a Sqon, you will mostly be working with the File implementation of PathInterface.

Event Subscribers

As documented in SqonInterface, the Sqon manager provides support for modifying certain processes using event listeners. The Sqon library provides a basic set of listeners that can be used to augment the Sqon building process. In addition to these listeners, you may create your own to use.

ChmodSubscriber

The ChmodSubscriber will change the permissions of the committed Sqon.

use Sqon\Event\Subscriber\ChmodSubscriber;

// Change permissions to 755 once committed.
$sqon->getEventDispatcher()->addSubscriber(
    new ChmodSubscriber(0755)
);

FilterSubscriber

Filters one or more paths from being set in the Sqon.

This subscriber will block one or more paths from being set in a Sqon by using exclusion and inclusion rules. If an exclusion rule is provided, any path matching the rule will be excluded regardless of any inclusion rules defined. If an inclusion rule is provided, all paths are excluded by default unless the path matches any one of the inclusion rules.

Both exclusion and inclusion rules relying on matching rules.

  • By Name A name is the last part of a path. If example is given to match, the path to/example will match but to/example/script.php will not match.
  • By Path A path is the beginning of any path. If example/to is given, any path that begins with example/to will match but diff/example/to will not match.
  • By Pattern A pattern is a regular expression. Any path that is matched by the regular expression will match. The expression must provide its own delimiter.
$subscriber = new FilterSubscriber();

// Exclude a path by name.
$subscriber->excludeByName('broken.php');

// Exclude an exact path.
$subscriber->excludeByPath('example/script.php');

// Exclude paths matching a pattern.
$subscriber->excludeByPattern('/[Tt]ests/');

// Only include paths with a specific name.
$subscriber->includeByName('LICENSE');

// Only include an exact path.
$subscriber->includeByPath('bin/example');

// Only include paths matching a pattern.
$subscriber->includeByPattern('/\.php$/');

$dispatcher->addSubscriber($subscriber);

Using the above rules, the following paths are stored in the Sqon:

  • LICENSE
  • bin/example
  • src/My/Example/Class.php

But the following paths were prevented from being stored:

  • broken.php
  • example/script.php
  • src/emoji.png
  • tests/My/Example/ClassTest.php

ReplaceSubscriber

Replaces the contents of some or all files.

This subscriber will replace patterns matched in the contents of paths before they are set in the Sqon. Patterns can be replaced globally, for specific paths, or for any path that matches another pattern. Multiple patterns can be replaced for each matching condition.

$subscriber = new ReplaceSubscriber();

// Adds a replacement for all paths.
$subscriber->replaceAll('/pattern/', 'replace');

// Adds a replacement for a specific path.
$subscriber->replaceByPath('path/to/script.php', '/pattern/', 'replace');

// Adds a replacement for any path matching a pattern.
$subscriber->replaceByPattern('/\.php$/', '/pattern/', 'replace');

$dispatcher->addSubscriber($subscriber);

Specification

Overview

A Sqon, pronounced "scone", is a PHP script with an embedded SQLite 3 database.

Purpose

A Sqon is a self-extracting archive for packaging PHP applications and libraries for distribution. With the inclusion of an initialization script, a Sqon can be used as a command line application, autoloading library, or a web application (through PHP-FPM or another equivalent).

Rationale

In 2012 I stumbled upon the Phar file format. After having used it for multiple projects, I came to realize that the process for creating new Phars was needlessly complicated. To simplify the process, I created the Box project which reduces many of the intricacies of creating Phars to a simple JSON configuration file. As the project picked up in popularity, there was a recurring theme in building Phars. The phar extension used to create Phars had not seen updates since 2009, which was evident in the types of bugs that were encountered while using Box. The community did many things to work around these issues, but ultimately the issues were there and biting those who did not expect or understand them.

I believe that the extension has stagnated because the maintainers are either too busy or have moved on, both reasons I can empathize with. I also believe that PHP developers capable of contributing patches in C are focused on other more pressing concerns with PHP.

I am hoping that by creating an alternative file format, it will be more accessible to developers for contributing bug fixes and even improvements. By using the battle tested SQLite database engine, I am hoping to avoid any issues relating to the storage and retrieval of data.

Format

A Sqon is divided into three parts:

  1. The PHP bootstrap script.
  2. The embedded SQLite database.
  3. The raw SHA-1 file signature.

Bootstrap

The PHP bootstrap script performs the following functions:

  1. Verifies that the signature for the Sqon is valid.
  2. Extracts the contents of the Sqon to a cache directory.
  3. Runs the primary script, if one exists.

Variables, constants, functions, and classes are not introduced into the global scope by the script. If supporting code is required for the bootstrapping process, it must exist in the Sqon\Bootstrap namespace. Using the prefix Sqon_Bootstrap_ for a function or class name is also acceptable.

If the Sqon is intended to be used as a command line application, a shebang line should be prepended to the PHP bootstrap script. Otherwise, the shebang line should be omitted to prevent PHP from printing it.

Signature Verification

The PHP bootstrap script will generate a new hashing using the contents of the Sqon, excluding the last 20 bytes. The hash is then compared to the last 20 bytes of the Sqon. If the hashes are not identical, the script will exit with an error stating that the Sqon has been corrupted.

Self Extraction

The PHP bootstrap script generates a deterministic cache directory path that is specific to the Sqon. The path is composed of the following information:

  1. The path to the system temporary directory.
  2. The hexadecimal SHA-1 hash signature of the Sqon.

Alternatively, the directory path defined by the SQON_TEMP environment variable can be used in place of the system temporary directory.

If the cache directory exists, the self extraction process is not performed. If the directory does not exist, the following operations are performed:

  1. The embedded database is extracted to a file called sqon.db.
  2. The file cache directory, files, is created.
  3. The files stored in the database are extracted to the file cache directory.
Primary Script

If the path .sqon/primary.php exists in the file cache directory, it will be loaded by the PHP bootstrap script in the global scope.

Database

The pdo_sqlite extension is required.

The SQLite 3 database immediately follows the __HALT_COMPILER(); call in the PHP bootstrap script. This allows the script to use the __COMPILER_HALT_OFFSET__ constant to easily locate the beginning of the database for extraction. If a new Sqon is created, the following schema is used to create the SQLite database:

-- The directories and files stored in the database.
CREATE TABLE paths (

    -- The unique path.
    path VARCHAR(4096) NOT NULL,

    -- The type of the path (directory or file).
    type INTEGER NOT NULL,

    -- The compression mode.
    compression INTEGER NOT NULL,

    -- The last modified Unix timestamp.
    modified INTEGER NOT NULL,

    -- The Unix file permissions as decimal.
    permissions INTEGER NOT NULL,

    -- The contents of the file.
    contents BLOB,

    PRIMARY KEY (path)
);

The following is information on what the schema definitions mean. For information on how this schema is actually used, you will want to read the Bootstrap section of the specification.

  • The path field is the canonicalized, relative path to the file or directory. All paths that begin with .sqon are reserved by the specification. The / (forward slash) is always used as the directory separator. All paths are case sensitive.
  • The type field indicates the type of the path (e.g. file, directory).
  • The compression field indicates if a file has been compressed and by what scheme.
  • The modified field is a Unix timestamp for when the contents of the file were last modified.
  • The permissions field is the numeric Unix permissions of the file encoded as decimal. If the permission for a file is 0644, the value is stored as 420.
  • The contents field is the compressed or decompressed contents of the file.

type Values

Value Description
0 The path is for a file.
1 The path is for a directory.

compression Values

Value Description
0 The contents are not compressed.
1 The contents were compressed using gzip.
2 The contents were compressed using bzip2.
Reservations

The SQLite database is reserved strictly for use by the Sqon specification. This is to prevent conflicts introduced through customizations of the database when an enhancement to the specification is defined or released.

Signature

The signature is a raw SHA-1 hash appended to the Sqon as the last 20 bytes of the file. This hash is generated by combining the PHP bootstrap script with the SQLite database.

This signature is intended to be used as a way of verifying file integrity. If a cryptographically secure signature is required, GPG or another equivalent must be used. It is recommended that a detached signature be created for secure distributions.

License

Sqon is released under both the MIT and Apache 2.0 licenses.

Logo

The scone in the logo was created by anbileru adaleru from the Noun Project.