PHPackages                             shudd3r/skeletons - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. shudd3r/skeletons

ActiveLibrary[Templating &amp; Views](/categories/templating)

shudd3r/skeletons
=================

Template engine for package skeletons

1.0.3(1y ago)11.5k↓100%1MITPHPPHP ^7.4 || ^8.0CI passing

Since Oct 31Pushed 1y ago1 watchersCompare

[ Source](https://github.com/shudd3r/skeletons)[ Packagist](https://packagist.org/packages/shudd3r/skeletons)[ RSS](/packages/shudd3r-skeletons/feed)WikiDiscussions develop Synced today

READMEChangelog (10)Dependencies (1)Versions (14)Used By (1)

Shudd3r/Skeletons
=================

[](#shudd3rskeletons)

[![Latest Stable Version](https://camo.githubusercontent.com/a6f2018f4d0ecf3e2a10404110162030f3b9482aec12fbd7128a37d02403b10b/68747470733a2f2f706f7365722e707567782e6f72672f736875646433722f736b656c65746f6e732f76657273696f6e)](https://packagist.org/packages/shudd3r/skeletons)[![Build status](https://github.com/shudd3r/skeletons/workflows/build/badge.svg)](https://github.com/shudd3r/skeletons/actions)[![Coverage status](https://camo.githubusercontent.com/5bd13196337888de7ee407bbe90bf982baaddfd11995ceb7ad5c34c10ee14be4/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f736875646433722f736b656c65746f6e732f62616467652e7376673f6272616e63683d646576656c6f70)](https://coveralls.io/github/shudd3r/skeletons?branch=develop)[![PHP version](https://camo.githubusercontent.com/1f46ba87637e5c7b2320f9115d1780451ec143bacaf0927270b259d52bd96105/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f736875646433722f736b656c65746f6e732e737667)](https://packagist.org/packages/shudd3r/skeletons)[![LICENSE](https://camo.githubusercontent.com/20932302768bf94167657ed72fe7162bff27f0630298cfb675029e0002b79652/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f736875646433722f736b656c65746f6e732e7376673f636f6c6f723d626c7565)](LICENSE)

### Template engine for package skeletons

[](#template-engine-for-package-skeletons)

***Feels like you're repeating yourself every time you start a new project?
This package might help you automate some stuff!***

Skeleton packages are used to maintain **consistent structure** of document (`LICENSE`, `README`etc.) &amp; dev environment files (tool configs, workflows, scripts) across multiple packages &amp; projects. This library allows building skeleton package scripts capable of:

- Generating file structure for deployed package &amp; local dev environment from skeleton files with built-in placeholder replacements or customized template processing scripts
- Verifying synchronization of existing project with chosen template as a part of [*continuous integration*](https://en.wikipedia.org/wiki/Continuous_integration) (*CI*) process
- Synchronizing mismatched &amp; missing files in existing project built with prepared skeleton script and its template files
- Updating template placeholder values used in existing package files

### Basic Usage

[](#basic-usage)

Neither applications nor libraries will use this package directly, but as a command line tool of a skeleton package they were built with - either tool application or project's composer dev binary.

To avoid conflicts it is released as a standalone package that doesn't use any production dependencies, and php version compatibility is the only limitation.

Before diving into details you can learn some basics about skeleton scripts by browsing files or playing with [*skeleton example*](https://github.com/shudd3r/skeleton-example) package.

#### Skeleton package

[](#skeleton-package)

Here's a list of main steps to create and use skeleton package - following sections will cover template &amp; script files in more details:

- Install this library as a dependency of your skeleton package using [*Composer*](https://getcomposer.org/) command: ```
    composer require shudd3r/skeletons
    ```
- Create template directory with your skeleton [template files](#template-files)
- Add CLI executable [script file](#executable-script-file) and `"bin"` directive in `composer.json`pointing to that file: ```
    {
      "bin": ["my-skeleton-script"]
    }
    ```
- Publish skeleton package and require it as a **dev dependency** in your projects
- Use skeleton as their desired file structure through [CLI commands](#command-line-usage)

### Executable script file

[](#executable-script-file)

Entire script that uses this library might look like [*`example-skeleton`*](https://github.com/shudd3r/skeleton-example/blob/develop/example-skeleton)file in `shudd3er/skeleton-example` repository.

##### Scripting features:

[](#scripting-features)

- Template placeholders are chosen by skeleton creators.
- Each placeholder is assigned to `Replacement` abstraction that manages user input, validation and resolving placeholder's (default) value.
- Replacement may also provide subtypes for defined placeholder (e.g. escaped slashes for `{namespace}`).
- `GenericReplacement` for simple replacements and fluent builder interface for easier instantiation.
- Placeholders for `{original.content}` with optional default mockup value.
- Possibility to customize template handling for individual files through `Template` abstraction.
- Synchronization that allows regenerating missing &amp; divergent files.
- Safe file operations - overwritten files are copied into backup directory.
- Filename extension directives that allow:
    - including (deactivating) files that could affect deployed skeleton files (e.g. `.gitignore`),
    - handling "local" files, that are not (or cannot be) part of published package,
    - using initial "mockup" files that can be removed or developed without breaking skeleton synchronization,
- Directories without specified files required by skeleton are managed by `.gitkeep` dummy files.

##### Setup steps:

[](#setup-steps)

- Instantiate input arguments and application:

    ```
    namespace Shudd3r\Skeletons;

    use Shudd3r\Skeletons\Environment\Files\Directory\LocalDirectory;

    $args = new InputArgs($argv);

    $package  = new LocalDirectory(get_cwd());
    $template = new LocalDirectory(__DIR__ . '/template');
    $app      = new Application($package, $template);
    ```
- Add [`Replacement`](src/Replacements/Replacement.php) definitions for placeholders used in templates:

    ```
    use Shudd3r\Skeletons\Replacements\Replacement;

    $app->replacement('package.name')->add(new Replacement\PackageName());
    $app->replacement('repository.name')->add(new Replacement\RepositoryName('package.name'));
    $app->replacement('package.description')->add(new Replacement\PackageDescription('package.name'));
    $app->replacement('namespace.src')->add(new Replacement\SrcNamespace('package.name'));
    ```

    Simple replacement definitions don't need to be implemented - [`GenericReplacement`](src/Replacements/Replacement/GenericReplacement.php) can be used instead:

    ```
    use Shudd3r\Skeletons\Replacements\Replacement\GenericReplacement;

    $default  = fn () => 'default@example.com';
    $validate = fn (string $value) => $value === filter_var($value, FILTER_VALIDATE_EMAIL);

    $app->replacement('author.email')
        ->add(new GenericReplacement($default, $validate, null, 'Your email address', 'email'));
    ```

    It can also be built using fluent builder invoked with `build()` method:

    ```
    $app->replacement('author.email')
        ->build(fn () => 'default@example.com')
        ->argumentName('email')
        ->inputPrompt('Your email address')
        ->validate(fn (string $value) => $value === filter_var($value, FILTER_VALIDATE_EMAIL));
    ```
- Setup custom [`Templates\Template`](src/Templates/Template.php) instantiation for selected template files\*:

    ```
    use Shudd3r\Skeletons\Templates\Contents;
    use Shudd3r\Skeletons\Templates\Template;

    $app->template('composer.json')->createWith(
        fn (Contents $contents) => new Template\MergedJsonTemplate(
            new Template\BasicTemplate($contents->template()),
            $contents->package(),
            $args->command() === 'update'
        )
    );
    ```

    \*Template that defines dynamic keys for [`MergedJsonTemplate`](src/Templates/Template/MergedJsonTemplate.php)(in case of `composer.json` it would usually be `namespace` placeholder) requires different merging algorithm for updates. If template doesn't use dynamic keys, its third (boolean flag) parameter is not required.
- Run application with `InputArgs`:

    ```
    $exitCode = $app->run($args);
    exit($exitCode);
    ```

### Command line usage

[](#command-line-usage)

```
vendor\bin\script-name  [] [=]

```

Available `` values:

- `init`: generates file structure from skeleton template (with backup on overwrite)
- `check`: verifies project synchronization with used skeleton
- `update`: changes current placeholder values (synchronized package required)
- `sync`: generates missing &amp; mismatched skeleton files (with backup on overwrite)
- `help`: displays help similar to this section

Application ``:

- `-i`, `--interactive`: allows providing placeholder values for `init` or `update` command via interactive shell. When no `=` is provided interactive mode is implicitly switched on.
- `-l`, `--local`: may be used to include files not deployed to remote repository if skeleton defines them (like git hooks or IDE settings). This option may be used for all file operations: initialization, validation, synchronization and updates. See `.sk_local` template filename suffix in [Directive suffixes](#directive-suffixes).

Available `` names depend on placeholders configured in application. Currently, built-in placeholders can receive their values from following arguments:

- `package`: package name (Packagist)
- `repo`: remote (GitHub) repository name
- `desc`: package description
- `ns`: project's main namespace

Values that contain spaces should be surrounded with double quotes. For example following command for [*`example-skeleton`*](https://github.com/shudd3r/skeleton-example/blob/develop/example-skeleton)script would update package description:

```
vendor/bin/example-skeleton update desc="New package description"
```

When both `--interactive` option and placeholder arguments are provided, valid argument values will become default for empty input.

### Template files

[](#template-files)

Project file structure controlled by skeleton will reflect template directory, and placeholders within its files will be replaced. Check out template directory in [example-template](tests/Fixtures/example-template)or one in [*skeleton example*](https://github.com/shudd3r/skeleton-example/tree/develop/template) package.

#### Directive suffixes

[](#directive-suffixes)

Behavior of some template files can be modified by adding a suffix to their names:

- `.sk_init` - files generated at initialization only, not verified. Usually used for example files.
- `.sk_local` - untracked, local dev environment files. Generated &amp; updated, but not verified on remote environments.
- `.sk_file` - deactivates files processed by remote workflows. For example `.gitignore` file cannot be safely deployed as a part of skeleton, because it might exclude some of its files.
- `.sk_dir` - similar to `.sk_file` in context of directories. For example `.git` directory cannot be deployed. Such directory is expected to contain `.sk_local` or `.sk_init` files.

#### Empty directories

[](#empty-directories)

Empty directories cannot be committed, but by convention `.gitkeep` dummy files may be used to enforce directory structure when no files are specified. Dummy files should be removed when other files in required directory are present.

Skeleton script will *create essential* or *remove redundant* `.gitkeep` files with `init`, `update`&amp; `sync` command, and package with redundant or missing dummy files will be **marked as invalid**by `check` operation. Contents of these files are not validated and [placeholders are not replaced](tests/Fixtures/example-files/package-after-sync/src/.gitkeep).

> In [example-template](tests/Fixtures/example-template) `src` and `tests` directories are required, but on initialization `src/Example.php` and `tests/ExampleTest.php` files will be created and `.gitkeep` file will be ignored, but when these files are removed (and no other file is added) `.gitkeep` will become necessary and `sync` command should be used to keep package compliant with the template.

#### Placeholders

[](#placeholders)

Placeholder consists of its name surrounded by curly braces. Script defines what kind of replacement given placeholder represents. For example, application configured the following way will replace `{namespace.src}` with given namespace value:

```
$app->replacement('namespace.src')->add(new Replacement\SrcNamespace());
```

##### Placeholder subtypes

[](#placeholder-subtypes)

Replacement, beside direct value may define its subtypes. For example [`SrcNamespace`](src/Replacements/Replacement/SrcNamespace.php) replacement defined above will also replace `{namespace.src.esc}` with escaped backslashes used in `composer.json` file, and [`PackageName`](src/Replacements/Replacement/PackageName.php) has `{*.composer}` subtype that gives normalized packagist package name in lowercase.

##### Original Content placeholder

[](#original-content-placeholder)

`{original.content}` is a special built-in placeholder that represents places where project specific text might appear. It's useful especially for README files, where skeleton cannot dictate its entire content. Template can also define **initial/default value** for it, and single template file can use this placeholder multiple times. For example README file that is expected to contain concrete sections might be defined as follows:

```
![{package.name} logo]({original.content>>>https://www.example.com/images/mockup-logo.png...Extended description here......
