diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml deleted file mode 100644 index aaf1d4f..0000000 --- a/.github/workflows/compatibility.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: compatibility - -on: - push: - branches: [master] - -jobs: - compatibility: - name: PHP ${{ matrix.php-versions }} on ${{ matrix.operating-system }} - runs-on: ${{ matrix.operating-system }} - if: "!contains(github.event.head_commit.message, '[ci skip]')" - strategy: - matrix: - operating-system: [ubuntu-latest] - php-versions: ['7.4', '8.0', '8.1', '8.2'] - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - coverage: xdebug - env: - COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Get composer cache directory - id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - uses: actions/cache@v2 - with: - path: ${{ steps.composercache.outputs.dir }} - key: ${{ runner.os }}-${{ matrix.php-versions }}-composer-${{ hashFiles('**/composer.json') }} - restore-keys: ${{ runner.os }}-${{ matrix.php-versions }}-composer- - - - name: Install Composer dependencies - run: composer install --no-progress --prefer-dist --optimize-autoloader --no-suggest - - - name: PHP lint - run: composer run-script lint diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 48974f4..c5566b5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,41 +1,36 @@ -name: main +name: Main -on: [pull_request] +on: + push: + branches: + - master + pull_request: + branches: + - master jobs: - main: - runs-on: ${{ matrix.operating-system }} + php: + name: PHP ${{ matrix.php }} + runs-on: ubuntu-latest + strategy: + fail-fast: true matrix: - operating-system: [ubuntu-latest] - php-versions: ['7.4', '8.0', '8.1', '8.2'] + php: [8.1, 8.2] + steps: - - name: Checkout + - name: Checkout code uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php-versions }} - coverage: xdebug - env: - COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Validate composer.json and composer.lock - run: composer validate --strict - - - name: Get composer cache directory - id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - uses: actions/cache@v3 - with: - path: ${{ steps.composercache.outputs.dir }} - key: ${{ runner.os }}-${{ matrix.php-versions }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.php-versions }}-composer- + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite + coverage: none - name: Install Composer dependencies - run: composer install --no-progress --prefer-dist --optimize-autoloader --no-suggest + run: composer install --prefer-dist --no-interaction --no-progress - - name: PHP lint - run: composer run-script lint + - name: Run Pint + run: vendor/bin/pint --test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 138a10b..38fe409 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: - name: Setup PHP, with composer and extensions uses: shivammathur/setup-php@v2 with: - php-version: '7.4' + php-version: '8.1' coverage: xdebug env: COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 61ead86..8b7ef35 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /vendor +composer.lock diff --git a/README.md b/README.md index c18dca4..b085d01 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![Latest Stable Version](https://img.shields.io/packagist/v/log1x/navi.svg?style=flat-square) ![Total Downloads](https://img.shields.io/packagist/dt/log1x/navi.svg?style=flat-square) -![Build Status](https://img.shields.io/github/actions/workflow/status/log1x/navi/compatibility.yml?branch=master&style=flat-square) +![Build Status](https://img.shields.io/github/actions/workflow/status/log1x/navi/main.yml?branch=master&style=flat-square) Hate the WordPress NavWalker? **Me too**. @@ -10,7 +10,7 @@ Navi is a developer-friendly alternative to the NavWalker. Easily build your Wor ## Requirements -- [PHP](https://secure.php.net/manual/en/install.php) >= 7.0 +- [PHP](https://secure.php.net/manual/en/install.php) >= 8.0 ## Installation @@ -37,7 +37,7 @@ Check out the [**examples**](examples) folder to see how to use Navi in your pro use Log1x\Navi\Navi; -$navigation = (new Navi())->build('primary_navigation'); +$navigation = Navi::make()->build('primary_navigation'); if ($navigation->isEmpty()) { return; diff --git a/composer.json b/composer.json index 0274deb..310cd8b 100644 --- a/composer.json +++ b/composer.json @@ -23,10 +23,10 @@ } }, "require": { - "php": "^7.0|^8.0" + "php": "^8.0" }, "require-dev": { - "squizlabs/php_codesniffer": "^3.4" + "laravel/pint": "^1.14" }, "extra": { "acorn": { @@ -37,10 +37,5 @@ "Navi": "Log1x\\Navi\\Facades\\Navi" } } - }, - "scripts": { - "lint": [ - "phpcs --ignore=vendor,examples --extensions=php --standard=PSR12 ." - ] } } diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 4184fcd..0000000 --- a/composer.lock +++ /dev/null @@ -1,101 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "a945d2f7b7d3b1d1b030612c1c3543ce", - "packages": [], - "packages-dev": [ - { - "name": "squizlabs/php_codesniffer", - "version": "3.9.0", - "source": { - "type": "git", - "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/d63cee4890a8afaf86a22e51ad4d97c91dd4579b", - "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" - }, - "bin": [ - "bin/phpcbf", - "bin/phpcs" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "Former lead" - }, - { - "name": "Juliette Reinders Folmer", - "role": "Current lead" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", - "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", - "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", - "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" - }, - "funding": [ - { - "url": "https://github.com/PHPCSStandards", - "type": "github" - }, - { - "url": "https://github.com/jrfnl", - "type": "github" - }, - { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" - } - ], - "time": "2024-02-16T15:06:51+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": "^7.0|^8.0" - }, - "platform-dev": [], - "plugin-api-version": "2.6.0" -} diff --git a/examples/sage-9/app/Controllers/App.php b/examples/sage-9/app/Controllers/App.php deleted file mode 100644 index f56a68e..0000000 --- a/examples/sage-9/app/Controllers/App.php +++ /dev/null @@ -1,16 +0,0 @@ -build('primary_navigation'); - } - - # ... -} diff --git a/examples/sage-9/resources/views/partials/navigation.blade.php b/examples/sage-9/resources/views/partials/navigation.blade.php deleted file mode 100644 index cfc9edf..0000000 --- a/examples/sage-9/resources/views/partials/navigation.blade.php +++ /dev/null @@ -1,23 +0,0 @@ -@if ($navigation->isNotEmpty()) -
-@endif diff --git a/examples/sage-10/app/View/Composers/Navigation.php b/examples/sage/app/View/Composers/Navigation.php similarity index 100% rename from examples/sage-10/app/View/Composers/Navigation.php rename to examples/sage/app/View/Composers/Navigation.php index 0f5c954..3d3f437 100644 --- a/examples/sage-10/app/View/Composers/Navigation.php +++ b/examples/sage/app/View/Composers/Navigation.php @@ -2,8 +2,8 @@ namespace App\View\Composers; -use Roots\Acorn\View\Composer; use Log1x\Navi\Facades\Navi; +use Roots\Acorn\View\Composer; class Navigation extends Composer { diff --git a/examples/sage-10/resources/views/partials/navigation.blade.php b/examples/sage/resources/views/partials/navigation.blade.php similarity index 100% rename from examples/sage-10/resources/views/partials/navigation.blade.php rename to examples/sage/resources/views/partials/navigation.blade.php diff --git a/examples/vanilla/template-parts/site-nav.php b/examples/vanilla/template-parts/site-nav.php index f79025b..5d14698 100644 --- a/examples/vanilla/template-parts/site-nav.php +++ b/examples/vanilla/template-parts/site-nav.php @@ -5,7 +5,7 @@ * @link https://github.com/log1x/navi */ -$navigation = new \Log1x\Navi\Navi()->build('primary-menu'); +$navigation = \Log1x\Navi\Navi::make()->build('primary-menu'); ?> isNotEmpty() ) : ?> diff --git a/pint.json b/pint.json new file mode 100644 index 0000000..2d41237 --- /dev/null +++ b/pint.json @@ -0,0 +1,8 @@ +{ + "exclude": [ + "examples/vanilla" + ], + "notName": [ + "plugin.php" + ] +} diff --git a/plugin.php b/plugin.php index 408cab4..4679568 100644 --- a/plugin.php +++ b/plugin.php @@ -4,12 +4,12 @@ * Plugin Name: Navi * Plugin URI: https://github.com/log1x/navi * Description: A developer-friendly alternative to the WordPress NavWalker. - * Version: 2.0.4 + * Version: 3.0.0 * Author: Brandon Nifong * Author URI: https://github.com/log1x */ -if (! file_exists($composer = __DIR__ . '/vendor/autoload.php')) { +if (! file_exists($composer = __DIR__.'/vendor/autoload.php')) { return; } diff --git a/src/Contracts/Arrayable.php b/src/Contracts/Arrayable.php deleted file mode 100644 index e8b73ff..0000000 --- a/src/Contracts/Arrayable.php +++ /dev/null @@ -1,13 +0,0 @@ - 'current', 'activeAncestor' => 'current_item_ancestor', 'activeParent' => 'current_item_parent', @@ -38,11 +34,9 @@ class MenuBuilder ]; /** - * The disallowed classes. - * - * @var array + * The disallowed menu classes. */ - protected $disallowedClasses = [ + protected array $disallowedClasses = [ 'current-menu', 'current_page', 'sub-menu', @@ -57,16 +51,21 @@ class MenuBuilder ]; /** - * Build a filtered array of objects containing the navigation menu items. - * - * @param array $menu - * @return array + * Make a new Menu Builder instance. */ - public function build($menu) + public static function make(): self { - $this->menu = $this->filter((array) $menu); + return new static; + } + + /** + * Build the navigation menu. + */ + public function build(array $menu = []): array + { + $this->menu = $this->filter($menu); - if (empty($this->menu)) { + if (! $this->menu) { return []; } @@ -75,38 +74,31 @@ public function build($menu) $this->menu ); - return $this->tree( + return $this->handle( $this->map($this->menu) ); } /** - * Filter the menu item's into a prepared collection. - * - * @param array $menu - * @return \Illuminate\Support\Collection + * Filter the menu items. */ - protected function filter($menu = []) + protected function filter(array $menu = []): array { - $menu = array_filter($menu, function ($item) { - return is_a($item, 'WP_Post') || is_a($item, 'WPML_LS_Menu_Item'); - }); + $menu = array_filter($menu, fn ($item) => is_a($item, 'WP_Post') || is_a($item, 'WPML_LS_Menu_Item')); - if (empty($menu)) { - return; + if (! $menu) { + return []; } _wp_menu_item_classes_by_context($menu); return array_map(function ($item) { - $classes = array_filter($item->classes, function ($class) { - return ! in_array($class, $this->disallowedClasses); - }); + $classes = array_filter($item->classes, fn ($class) => ! in_array($class, $this->disallowedClasses)); $item->classes = is_array($classes) ? implode(' ', $classes) : $classes; foreach ($item as $key => $value) { - if (empty($value)) { + if (! $value) { $item->{$key} = false; } } @@ -116,12 +108,9 @@ protected function filter($menu = []) } /** - * Map the menu item properties into a fluent object. - * - * @param array $menu - * @return \Illuminate\Support\Collection + * Map the menu items into an object. */ - protected function map($menu = []) + protected function map(array $menu = []): array { return array_map(function ($item) { $result = []; @@ -130,34 +119,33 @@ protected function map($menu = []) $result[$key] = $item->{$value}; } - $result['parentObjectId'] = ! empty($result['parent']) && ! empty($this->menu[$result['parent']]) ? - $this->menu[$result['parent']]->object_id : - false; + $result['parentObjectId'] = ! empty($result['parent']) && ! empty($this->menu[$result['parent']]) + ? $this->menu[$result['parent']]->object_id + : false; return (object) $result; }, $menu); } /** - * Build a multi-dimensional array containing children menu items. - * - * @param object $items - * @param int $parent - * @param array $branch - * @return array + * Handle the menu item hierarchy. */ - protected function tree($items, $parent = 0, $branch = []) + protected function handle(array $items, int $parent = 0): array { - foreach ($items as $item) { - if ($item->parent == $parent) { - $children = $this->tree($items, $item->id); - $item->children = ! empty($children) ? $children : []; + $menu = []; - $branch[$item->order] = $item; - unset($item); + foreach ($items as $item) { + if ($item->parent != $parent) { + continue; } - }; - return $branch; + $item->children = $this->handle($items, $item->id); + + $menu[$item->id] = $item; + + unset($item); + } + + return $menu; } } diff --git a/src/Navi.php b/src/Navi.php index da013bd..6769861 100644 --- a/src/Navi.php +++ b/src/Navi.php @@ -2,34 +2,22 @@ namespace Log1x\Navi; -use ArrayAccess; -use JsonSerializable; -use Log1x\Navi\Contracts\Arrayable; -use Log1x\Navi\Contracts\Jsonable; - -class Navi implements Arrayable, ArrayAccess, Jsonable, JsonSerializable +class Navi { /** * The menu object. - * - * @var mixed */ - protected $menu; + protected mixed $menu; /** * The menu items. - * - * @var array */ - protected $items = []; + protected array $items = []; /** * Create a new Navi instance. - * - * @param array|object $items - * @return void */ - public function __construct($items = []) + public function __construct(array $items = []) { foreach ($items as $key => $value) { $this->items[$key] = $value; @@ -37,12 +25,17 @@ public function __construct($items = []) } /** - * Build and assign the navigation menu items to the Navi instance. - * - * @param int|string|\WP_Term $menu - * @return $this + * Make a new Navi instance. */ - public function build($menu = 'primary_navigation') + public static function make(array $items = []): self + { + return new static($items); + } + + /** + * Build the navigation menu items. + */ + public function build(string $menu = 'primary_navigation'): self { if (is_string($menu)) { $locations = get_nav_menu_locations(); @@ -58,21 +51,18 @@ public function build($menu = 'primary_navigation') $this->menu = wp_get_nav_menu_object($menu); - $this->items = (new MenuBuilder())->build( - wp_get_nav_menu_items($this->menu) - ); + $items = wp_get_nav_menu_items($this->menu); + + $this->items = MenuBuilder::make()->build($items ?? []); return $this; } /** - * Returns the current navigation menu object. - * - * @param string $key - * @param mixed $default - * @return mixed + * Retrieve the specified key from the WordPress menu object. + * If no key is specified, the entire menu object will be returned. */ - public function get($key = null, $default = null) + public function get(?string $key = null, mixed $default = null): mixed { if (! $this->menu) { return $default; @@ -86,172 +76,50 @@ public function get($key = null, $default = null) } /** - * Determine whether the Navi instance is empty. - * - * @return bool + * Determine if Navi is empty. */ - public function isEmpty() + public function isEmpty(): bool { - return empty($this->items); + return empty($this->all()); } /** - * Determine whether the Navi instance is not empty. - * - * @return bool + * Determine if Navi is not empty. */ - public function isNotEmpty() + public function isNotEmpty(): bool { return ! $this->isEmpty(); } /** - * Get the items from the Navi instance. - * - * @return array + * Retrieve the Navi items. */ - public function getItems() + public function all(): array { return $this->items; } /** - * Convert the Navi instance to an array. - * - * @return array - */ - public function toArray() - { - return $this->items; - } - - /** - * Convert the object into something JSON serializable. - * - * @return array - */ - #[\ReturnTypeWillChange] - public function jsonSerialize() - { - return $this->toArray(); - } - - /** - * Convert the Navi instance to JSON. - * - * @param int $options - * @return string - */ - public function toJson($options = 0) - { - return json_encode($this->jsonSerialize(), $options); - } - - /** - * Determine if the given offset exists. - * - * @param string $offset - * @return bool - */ - #[\ReturnTypeWillChange] - public function offsetExists($offset) - { - return isset($this->attributes[$offset]); - } - - /** - * Get the value for a given offset. - * - * @param string $offset - * @return mixed + * Retrieve the Navi items as an array. */ - #[\ReturnTypeWillChange] - public function offsetGet($offset) + public function toArray(): array { - return $this->get($offset); + return $this->all(); } /** - * Set the value at the given offset. - * - * @param string $offset - * @param mixed $value - * @return void + * Retrieve the Navi items as JSON. */ - #[\ReturnTypeWillChange] - public function offsetSet($offset, $value) + public function toJson(int $options = 0): string { - $this->attributes[$offset] = $value; + return json_encode($this->toArray(), $options); } /** - * Unset the value at the given offset. - * - * @param string $offset - * @return void + * Dynamically retrieve a Navi item. */ - #[\ReturnTypeWillChange] - public function offsetUnset($offset) - { - unset($this->attributes[$offset]); - } - - /** - * Handle dynamic calls to the Navi instance to set items. - * - * @param string $method - * @param array $parameters - * @return $this - */ - public function __call($method, $parameters) - { - $this->items[$method] = count($parameters) > 0 ? $parameters[0] : true; - - return $this; - } - - /** - * Dynamically retrieve the value of an attribute. - * - * @param string $key - * @return mixed - */ - public function __get($key) + public function __get(string $key): mixed { return $this->get($key); } - - /** - * Dynamically set the value of an attribute. - * - * @param string $key - * @param mixed $value - * @return void - */ - public function __set($key, $value) - { - $this->offsetSet($key, $value); - } - - /** - * Dynamically check if an attribute is set. - * - * @param string $key - * @return bool - */ - public function __isset($key) - { - return $this->offsetExists($key); - } - - /** - * Dynamically unset an attribute. - * - * @param string $key - * @return void - */ - public function __unset($key) - { - $this->offsetUnset($key); - } } diff --git a/src/Providers/NaviServiceProvider.php b/src/Providers/NaviServiceProvider.php index aee6cad..f012cdc 100644 --- a/src/Providers/NaviServiceProvider.php +++ b/src/Providers/NaviServiceProvider.php @@ -2,8 +2,8 @@ namespace Log1x\Navi\Providers; -use Log1x\Navi\Navi; use Illuminate\Support\ServiceProvider; +use Log1x\Navi\Navi; class NaviServiceProvider extends ServiceProvider { @@ -14,8 +14,6 @@ class NaviServiceProvider extends ServiceProvider */ public function register() { - $this->app->bind('navi', function () { - return new Navi(); - }); + $this->app->bind('navi', fn () => Navi::make()); } }