Skip to content

Bespoke standards for the Wayne State app stack

Pinstripe

Sharp code. Every line.

Shared presets for Pint, PHPStan with Larastan, Rector, ESLint for Vue and Prettier, cut once and worn by every repo in the stack and every app it generates. Your app points at them and adds only its own exceptions.

Book a fitting Try it on today

On Packagist and npm now. The shop is open; walk-ins welcome.

Scroll, and the tailor gets to work

The fitting

One PHP class before and after the fitting. Before: the constructor assigns a property by hand, greet() has no return type and its brace on the same line, the array uses array(), and strlen() is given a value that might be null. After: the property is promoted into the constructor, greet() returns string with its brace on its own line, the array is short, and the null case is handled.

As it walked in

final class Greeter
{
    private string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function greet() {
        $parts = array('Hello', $this->name);
        return implode(', ', $parts);
    }

    public function length(?string $nick): int
    {
        return strlen($nick);
    }
}

Pressed

final class Greeter
{
    public function __construct(private string $name)
    {
    }

    public function greet(): string
    {
        $parts = ['Hello', $this->name];
        return implode(', ', $parts);
    }

    public function length(?string $nick): int
    {
        return strlen($nick ?? '');
    }
}

Alterations No. 0.1

  1. Rector Promote the property into the constructor.
  2. Rector Add the return type, : string.
  3. Pint The method's brace goes on its own line (PER).
  4. Rector array() becomes [].
  5. PHPStan · level 8 $nick might be null. PHPStan points; you sew the fix.

Pressed 0 errors at level 8. Nothing changed but the fit.

Cut from Pinstripe's own test fixtures (raw-messy, rector-old, raw-nullbug), where the real tools run on every composer ci.

Every preset is a piece of the suit

The pieces

Seven measurements, one order card. Status comes straight from BEST_PRACTICES.md: a piece is Enforced when a test runs the real tool and checks both directions.

  1. The cutPint
    pint/per.json pint/laravel.json

    PER for raw PHP, the Laravel preset for Laravel.

    Enforced
  2. The inseamPHPStan
    phpstan/raw.neon phpstan/laravel.neon

    Level 8 for new code. Every ignore needs a comment.

    Enforced
  3. The liningLarastan
    phpstan/laravel.neon

    Sews itself in through the extension installer. No include line to keep.

    Enforced
  4. The pressRector
    rector/base.php

    Reads your PHP floor from your own composer.json. Dead code, code quality, types, early returns.

    Enforced
  5. The buttonholesESLint
    eslint/javascript.js eslint/typescript.js

    Vue 3 recommended plus vuejs-accessibility. Every image gets its alt.

    Enforced
  6. The hemPrettier
    prettier/index.json

    Single quotes, semicolons, 100 columns, trailing commas.

    Draft
  7. The chalk lineBaseline
    phpstan analyse --generate-baseline

    For an existing app: mark the fabric as it is, then take it in every release. Never let it out.

    Yours

The hem is still a draft: no test runs Prettier yet, so it is tacked, not sewn.

Every piece has a mirror

A preset without a test is a suit without a mirror: you can't tell when it has gone wrinkled. So each one is tried on twice.

It fits

  • raw-clean passes Pint and PHPStan
  • laravel-app passes the Laravel presets
  • rector-clean is left alone by Rector
  • clean-js and clean-ts lint clean

It catches

  • raw-messy fails Pint
  • raw-nullbug: PHPStan reports the null
  • rector-old gets altered
  • an <img> without alt, a v-for without :key

One bolt of cloth, three patterns

Made to measure

Raw PHP, Laravel and Vue each get their own pattern. These are the files your app keeps; each pattern starts with its install line.

Piece A · cut 1

Raw PHP

No framework. Pint with PER, PHPStan at level 8.

Install

composer require --dev waynestate/pinstripe:^0.1

Pinstripe 0.1.0 is on Packagist, so the line carries ^0.1; bump it when a newer version ships.

phpstan.neon

includes:
    - vendor/waynestate/pinstripe/phpstan/raw.neon

parameters:
    paths:
        - app

composer.json, scripts

"lint": "pint --test --config vendor/waynestate/pinstripe/pint/per.json",
"fix": "pint --config vendor/waynestate/pinstripe/pint/per.json",
"analyse": "phpstan analyse"

Composer scripts, so they run the same on Windows, macOS and Linux.

Piece B · cut 1

Laravel

The Laravel Pint preset, PHPStan with the Larastan lining.

Install

composer require --dev waynestate/pinstripe:^0.1 larastan/larastan

phpstan.neon

includes:
    - vendor/waynestate/pinstripe/phpstan/laravel.neon

That's the whole file. Larastan registers itself through phpstan/extension-installer; allow the plugin when Composer asks. Scripts point at pint/laravel.json.

Proven on a minimal Laravel 13 app in the fixtures, every run.

Piece C · cut 1

Vue

ESLint flat config with vuejs-accessibility, Prettier for the layout.

Install

npm install --save-dev @waynestate/pinstripe eslint prettier

eslint.config.js

import pinstripe from '@waynestate/pinstripe/eslint/typescript'

export default [...pinstripe]

Plain JavaScript? Import eslint/javascript instead.

package.json

"prettier": "@waynestate/pinstripe/prettier"

Alterations · fits A and B

Rector

Rector modernises code for the PHP version your app already supports. It reads the floor from your own composer.json, so it can't drift out of step. The press smooths old code to the standard without changing the cut.

Install, alongside Pinstripe

composer require --dev rector/rector

rector.php

<?php

$base = require __DIR__ . '/vendor/waynestate/pinstripe/rector/base.php';

return $base->withPaths([__DIR__ . '/src']);

Real today

Try it on

It's on the rack now; you can still try Pinstripe on in the shop: clone it and run its own checks. composer ci runs Pint, PHPStan, Larastan and Rector against the fixtures; npm test runs the ESLint presets.

The same commands on Windows, macOS and Linux. You need PHP 8.3 or newer, Composer, and Node 22.13 or newer.

On the cutting table next PREFLIGHT jobs, and reading the PHP floor from CREED. coming soon

Fitting room any OS

$ git clone https://github.com/waynestate/pinstripe.git
$ cd pinstripe
$ composer install
$ composer ci
$ npm ci
$ npm test

Expect green on PHP 8.3, 8.4 and 8.5.

The whole wardrobe

Worn across the stack

Every repo on this rail, and every app WELD and Sourdough turn out, is cut to Pinstripe.