{"id":10020928,"date":"2025-08-01T00:15:18","date_gmt":"2025-07-31T16:15:18","guid":{"rendered":"https:\/\/itmax.cn\/?p=10020928"},"modified":"2025-08-01T00:15:18","modified_gmt":"2025-07-31T16:15:18","slug":"php-cs-fixer-%e9%9b%86%e6%88%90-blade-formatter-%e6%9d%a5%e6%a0%bc%e5%bc%8f%e5%8c%96-blade-%e6%a8%a1%e6%9d%bf","status":"publish","type":"post","link":"https:\/\/itmax.cn\/?p=10020928","title":{"rendered":"php-cs-fixer \u96c6\u6210 blade-formatter \u6765\u683c\u5f0f\u5316 blade \u6a21\u677f"},"content":{"rendered":"<h2>php-cs-fixer \u96c6\u6210 blade-formatter \u6765\u683c\u5f0f\u5316 blade \u6a21\u677f<\/h2>\n<h3>\u51c6\u5907\u5de5\u4f5c<\/h3>\n<p>\u5b89\u88c5 <a href=\"https:\/\/link.segmentfault.com\/?enc=84hr5A1e68kh%2FCgcRrChvA%3D%3D.XMo8Yd8KyEUIYjSGzhPXtUBs3FY%2BmyNbi7tizbReq6O8yWdNdOO9xG9s32ZDq4nX\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">PHP-CS-Fixer<\/a> \u548c <a href=\"https:\/\/link.segmentfault.com\/?enc=IxLWLsNfCSbQVgKjzR1%2FeA%3D%3D.z7kQ8myoi5hJ8HeapzUNUT7vggsUoYE0z6DED7YPpTdUcsyDZ5Mt87vpvr0exp9K\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">blade-formatter<\/a><\/p>\n<pre><code class=\"shell\">composer require --dev friendsofphp\/php-cs-fixer\nnpm install -g blade-formatter<\/code><\/pre>\n<h3>\u521b\u5efa\u4fee\u590d\u5668 <a href=\"https:\/\/link.segmentfault.com\/?enc=b5OsT8dbV%2BRaL19PqkUJTg%3D%3D.LgrsXxYuBFrJYaKtxySDrtAMCMAGtnSYGskQxjQ%2Ff598PwgG%2FO%2FLZko0WCV0CruEYKGiHBLsexXVdgiX6mYYnTv9WmjHtqTjqUsYB%2BSJC%2BiDl23PAORNuuAXw6V9H%2Fx9ceiZEoLd98pHOjRK0HW8eQ%3D%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">BladeFixer.php<\/a><\/h3>\n<pre><code class=\"php\">&lt;?php\n\n\/** @noinspection MissingParentCallInspection *\/\n\/** @noinspection PhpConstantNamingConventionInspection *\/\n\/** @noinspection PhpDeprecationInspection *\/\n\/** @noinspection PhpInternalEntityUsedInspection *\/\n\/** @noinspection PhpMissingParentCallCommonInspection *\/\n\/** @noinspection SensitiveParameterInspection *\/\n\ndeclare(strict_types=1);\n\n\/**\n * Copyright (c) 2021-2025 guanguans&lt;ityaozm@gmail.com&gt;\n *\n * For the full copyright and license information, please view\n * the LICENSE file that was distributed with this source code.\n *\n * @see https:\/\/github.com\/guanguans\/laravel-skeleton\n *\/\n\nnamespace App\/Support\/PhpCsFixer\/Fixer;\n\nuse PhpCsFixer\/AbstractFixer;\nuse PhpCsFixer\/FileReader;\nuse PhpCsFixer\/FileRemoval;\nuse PhpCsFixer\/Fixer\/ConfigurableFixerInterface;\nuse PhpCsFixer\/Fixer\/ConfigurableFixerTrait;\nuse PhpCsFixer\/FixerConfiguration\/FixerConfigurationResolver;\nuse PhpCsFixer\/FixerConfiguration\/FixerConfigurationResolverInterface;\nuse PhpCsFixer\/FixerConfiguration\/FixerOptionBuilder;\nuse PhpCsFixer\/FixerDefinition\/CodeSample;\nuse PhpCsFixer\/FixerDefinition\/FixerDefinition;\nuse PhpCsFixer\/FixerDefinition\/FixerDefinitionInterface;\nuse PhpCsFixer\/Tokenizer\/Tokens;\nuse PhpCsFixer\/Utils;\nuse Symfony\/Component\/Console\/Application;\nuse Symfony\/Component\/Console\/Input\/ArgvInput;\nuse Symfony\/Component\/Console\/Input\/InputInterface;\nuse Symfony\/Component\/Console\/Output\/ConsoleOutput;\nuse Symfony\/Component\/Console\/Output\/OutputInterface;\nuse Symfony\/Component\/Console\/Style\/SymfonyStyle;\nuse Symfony\/Component\/OptionsResolver\/OptionsResolver;\nuse Symfony\/Component\/Process\/Exception\/ProcessFailedException;\nuse Symfony\/Component\/Process\/ExecutableFinder;\nuse Symfony\/Component\/Process\/Process;\n\n\/**\n * @see https:\/\/github.com\/shufo\/blade-formatter\n *\n * @property array{\n *     command: array,\n *     options: array,\n *     cwd: ?string,\n *     env: ?array,\n *     input: ?string,\n *     timeout: ?float\n * } $configuration\n *\n * @method void configureIO(InputInterface $input, OutputInterface $output)\n *\/\nfinal class BladeFixer extends AbstractFixer implements ConfigurableFixerInterface\n{\n    use ConfigurableFixerTrait;\n    public const string COMMAND = 'command';\n    public const string OPTIONS = 'options';\n    public const string CWD = 'cwd';\n    public const string ENV = 'env';\n    public const string INPUT = 'input';\n    public const string TIMEOUT = 'timeout';\n\n    #[\/Override]\n    public function getDefinition(): FixerDefinitionInterface\n    {\n        return new FixerDefinition(\n            $summary = \/sprintf('Format a [%s] file.', $this-&gt;getShortHeadlineName()),\n            [new CodeSample($summary)]\n        );\n    }\n\n    public static function name(): string\n    {\n        return (new self)-&gt;getName();\n    }\n\n    #[\/Override]\n    public function getName(): string\n    {\n        return \/sprintf('User\/%s', $this-&gt;getShortName());\n    }\n\n    public function getShortHeadlineName(): string\n    {\n        return str($this-&gt;getShortName())-&gt;headline()-&gt;toString();\n    }\n\n    public function getShortName(): string\n    {\n        return parent::getName();\n    }\n\n    #[\/Override]\n    public function isRisky(): bool\n    {\n        return true;\n    }\n\n    #[\/Override]\n    public function getPriority(): int\n    {\n        return \/PHP_INT_MAX;\n    }\n\n    \/**\n     * @param \/PhpCsFixer\/Tokenizer\/Tokens&lt;\/PhpCsFixer\/Tokenizer\/Token&gt; $tokens\n     *\/\n    #[\/Override]\n    public function isCandidate(Tokens $tokens): bool\n    {\n        return $tokens-&gt;count() === 1 &amp;&amp; $tokens[0]-&gt;isGivenKind(\/T_INLINE_HTML);\n    }\n\n    #[\/Override]\n    public function supports(\/SplFileInfo $file): bool\n    {\n        return str_ends_with($file-&gt;getBasename(), '.blade.php');\n    }\n\n    protected function createConfigurationDefinition(): FixerConfigurationResolverInterface\n    {\n        return new FixerConfigurationResolver([\n            (new FixerOptionBuilder(self::COMMAND, 'The blade-formatter command to run.'))\n                -&gt;setAllowedTypes(['string', 'array'])\n                -&gt;setDefault('blade-formatter')\n                -&gt;setNormalizer(static fn (OptionsResolver $optionsResolver, array|string $value): array =&gt; array_map(\n                    static fn (string $value): string =&gt; str_contains($value, \/DIRECTORY_SEPARATOR)\n                        ? $value\n                        : (new ExecutableFinder)-&gt;find($value, $value),\n                    (array) $value,\n                ))\n                -&gt;getOption(),\n            (new FixerOptionBuilder(self::OPTIONS, 'The options to pass to the command.'))\n                -&gt;setAllowedTypes(['array'])\n                -&gt;setDefault([])\n                -&gt;setNormalizer(static fn (OptionsResolver $optionsResolver, array $value): array =&gt; collect($value)-&gt;reduce(\n                    static function (array $options, mixed $value, int|string $key): array {\n                        \/is_string($key) and str_starts_with($key, '-') and $options[] = $key;\n                        $options[] = $value;\n\n                        return $options;\n                    },\n                    []\n                ))\n                -&gt;getOption(),\n            (new FixerOptionBuilder(self::CWD, 'The working directory or null to use the working dir of the current PHP process.'))\n                -&gt;setAllowedTypes(['string', 'null'])\n                -&gt;setDefault(null)\n                -&gt;getOption(),\n            (new FixerOptionBuilder(self::ENV, 'The environment variables or null to use the same environment as the current PHP process.'))\n                -&gt;setAllowedTypes(['array', 'null'])\n                -&gt;setDefault(null)\n                -&gt;getOption(),\n            (new FixerOptionBuilder(self::INPUT, 'The input as stream resource, scalar or \/Traversable, or null for no input.'))\n                -&gt;setAllowedTypes(['string', 'null'])\n                -&gt;setDefault(null)\n                -&gt;getOption(),\n            (new FixerOptionBuilder(self::TIMEOUT, 'The timeout in seconds or null to disable.'))\n                -&gt;setAllowedTypes(['float', 'int', 'null'])\n                -&gt;setDefault(10)\n                -&gt;getOption(),\n        ]);\n    }\n\n    \/**\n     * @param \/PhpCsFixer\/Tokenizer\/Tokens&lt;\/PhpCsFixer\/Tokenizer\/Token&gt; $tokens\n     *\n     * @throws \/Throwable\n     *\/\n    #[\/Override]\n    protected function applyFix(\/SplFileInfo $file, Tokens $tokens): void\n    {\n        $process = new Process(\n            command: [\n                ...$this-&gt;configuration[self::COMMAND],\n                $finalPath = $this-&gt;finalFile($file, $tokens),\n                '--write',\n                ...$this-&gt;configuration[self::OPTIONS],\n            ],\n            cwd: $this-&gt;configuration[self::CWD],\n            env: $this-&gt;configuration[self::ENV],\n            input: $this-&gt;configuration[self::INPUT],\n            timeout: $this-&gt;configuration[self::TIMEOUT],\n        );\n        $process-&gt;run();\n        $this-&gt;debugProcess($process);\n\n        if (!$process-&gt;isSuccessful()) {\n            throw new ProcessFailedException($process);\n        }\n\n        $tokens-&gt;setCode(FileReader::createSingleton()-&gt;read($finalPath));\n    }\n\n    \/**\n     * @noinspection GlobalVariableUsageInspection\n     *\n     * @param \/PhpCsFixer\/Tokenizer\/Tokens&lt;\/PhpCsFixer\/Tokenizer\/Token&gt; $tokens\n     *\/\n    private function finalFile(\/SplFileInfo $file, Tokens $tokens): string\n    {\n        $finalFile = (string) $file;\n\n        if (\/in_array('--dry-run', $_SERVER['argv'] ?? [], true)) {\n            file_put_contents($finalFile = $this-&gt;createTemporaryFile(), $tokens-&gt;generateCode());\n        }\n\n        return $finalFile;\n    }\n\n    private function createTemporaryFile(): string\n    {\n        static $temporaryFile;\n\n        if ($temporaryFile) {\n            return $temporaryFile;\n        }\n\n        $temporaryFile = tempnam($tempDir = sys_get_temp_dir(), &quot;{$this-&gt;getShortName()}_&quot;);\n\n        if (!$temporaryFile) {\n            throw new \/RuntimeException(&quot;The temporary file could not be created in the temporary directory [$tempDir].&quot;);\n        }\n\n        (new FileRemoval)-&gt;observe($temporaryFile);\n\n        return $temporaryFile;\n    }\n\n    \/**\n     * @noinspection DuplicatedCode\n     *\/\n    private function debugProcess(Process $process): void\n    {\n        if (!($symfonyStyle = $this-&gt;createSymfonyStyle())-&gt;isDebug()) {\n            return;\n        }\n\n        $symfonyStyle-&gt;title(&quot;Process debugging information for [{$this-&gt;getName()}]&quot;);\n        $symfonyStyle-&gt;warning([\n            \/sprintf('Command Line: %s', $process-&gt;getCommandLine()),\n            \/sprintf('Exit Code: %s', Utils::toString($process-&gt;getExitCode())),\n            \/sprintf('Exit Code Text: %s', Utils::toString($process-&gt;getExitCodeText())),\n            \/sprintf('Output: %s', $process-&gt;getOutput()),\n            \/sprintf('Error Output: %s', $process-&gt;getErrorOutput()),\n            \/sprintf('Working Directory: %s', Utils::toString($process-&gt;getWorkingDirectory())),\n            \/sprintf('Env: %s', Utils::toString($process-&gt;getEnv())),\n            \/sprintf('Input: %s', Utils::toString($process-&gt;getInput())),\n            \/sprintf('Timeout: %s', Utils::toString($process-&gt;getTimeout())),\n        ]);\n    }\n\n    private function createSymfonyStyle(): SymfonyStyle\n    {\n        $argvInput = new ArgvInput;\n        $consoleOutput = new ConsoleOutput;\n\n        \/\/ to configure all -v, -vv, -vvv options without memory-lock to Application run() arguments\n        (fn () =&gt; $this-&gt;configureIO($argvInput, $consoleOutput))-&gt;call(new Application);\n\n        return new SymfonyStyle($argvInput, $consoleOutput);\n    }\n}<\/code><\/pre>\n<h3>\u914d\u7f6e\u4fee\u590d\u5668 <a href=\"https:\/\/link.segmentfault.com\/?enc=nOa8kVZEfPwCXTZ%2BIKtoyg%3D%3D.X%2FpvONdIaXueI2ELBPyswZwzREEVxB4mB5F53YWvbgTl8W00SLJyeuJjGyQOoXBs5a8Nz5fXD6YuKWCrcnEH9gPQLnPToT2YFj86KIYpLVapVUtT8LPLr1G7LkusWFm4Kp5oIVfEPMad9w1%2Bgwbjcw%3D%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">BladeFixer.php<\/a><\/h3>\n<pre><code class=\"php\">&lt;?php\n\ndeclare(strict_types=1);\n\n\/**\n * Copyright (c) 2021-2025 guanguans&lt;ityaozm@gmail.com&gt;\n *\n * For the full copyright and license information, please view\n * the LICENSE file that was distributed with this source code.\n *\n * @see https:\/\/github.com\/guanguans\/laravel-skeleton\n *\/\n\nuse App\/Support\/PhpCsFixer\/Fixer\/BladeFixer;\nuse PhpCsFixer\/Config;\n\nreturn (new Config)\n    \/\/ ... \u5176\u4ed6\u914d\u7f6e\n    -&gt;registerCustomFixers([\n        new BladeFixer,\n    ])\n    \/\/ ... \u5176\u4ed6\u914d\u7f6e\n    -&gt;setRules([\n        \/\/ ... \u5176\u4ed6\u89c4\u5219\u914d\u7f6e\n\n        \/\/ \/\/ \u81ea\u5b9a\u4e49 BladeFixer \u89c4\u5219\u914d\u7f6e\n        \/\/ BladeFixer::name() =&gt; [\n        \/\/     BladeFixer::COMMAND =&gt; ['path\/to\/node', 'path\/to\/blade-formatter'],\n        \/\/     BladeFixer::OPTIONS =&gt; [\n        \/\/         '--config' =&gt; 'path\/to\/.bladeformatterrc',\n        \/\/         '--indent-size' =&gt; 2,\n        \/\/         \/\/ ...\n        \/\/     ],\n        \/\/ ],\n        \/\/ \u9ed8\u8ba4 BladeFixer \u89c4\u5219\u914d\u7f6e\n        BladeFixer::name() =&gt; true,\n\n        \/\/ ... \u5176\u4ed6\u89c4\u5219\u914d\u7f6e\n    ])\n    \/\/ ... \u5176\u4ed6\u914d\u7f6e\n    -&gt;setRiskyAllowed(true);<\/code><\/pre>\n<h3>\u6d4b\u8bd5\u793a\u4f8b<\/h3>\n<pre><code class=\"diff\">\u2570\u2500 vendor\/bin\/php-cs-fixer fix --config=.php-cs-fixer.php --using-cache=no --diff --dry-run --ansi -vv tests.blade.php      \u2500\u256f\nPHP CS Fixer 3.84.0 Alexander by Fabien Potencier, Dariusz Ruminski and contributors.\nPHP runtime: 8.3.23\nRunning analysis on 7 cores with 10 files per process.\nParallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!\nLoaded config ergebnis (PHP 8.3) (future mode) from &quot;.php-cs-fixer.php&quot;.\nPaths from configuration file have been overridden by paths provided as command arguments.\n 1\/1 [\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593] 100%\n\n   1) tests.blade.php (User\/blade)\n      ---------- begin diff ----------\n--- \/Users\/yaozm\/Documents\/wwwroot\/laravel-skeleton\/tests.blade.php\n+++ \/Users\/yaozm\/Documents\/wwwroot\/laravel-skeleton\/tests.blade.php\n@@ -10,16 +10,16 @@\n             &lt;\/div&gt;\n             &lt;div class=&quot;pf-users-branch&quot;&gt;\n                 &lt;ul class=&quot;pf-users-branch__list&quot;&gt;\n-                    @foreach($users as $user)\n+                    @foreach ($users as $user)\n                         &lt;li&gt;\n                             &lt;img src=&quot;{{ asset('img\/frontend\/icon\/branch-arrow.svg') }}&quot; alt=&quot;branch_arrow&quot;&gt;\n-                            {{ link_to_route(&quot;frontend.users.user.show&quot;,$users[&quot;name&quot;],$users['_id']) }}\n+                            {{ link_to_route('frontend.users.user.show', $users['name'], $users['_id']) }}\n                         &lt;\/li&gt;\n                     @endforeach\n                 &lt;\/ul&gt;\n                 &lt;div class=&quot;pf-users-branch__btn&quot;&gt;\n                     @can('create', App\/Models\/User::class)\n-                        {!! link_to_route(&quot;frontend.users.user.create&quot;,__('users.create'),[1,2,3],['class' =&gt; 'btn']) !!}\n+                        {!! link_to_route('frontend.users.user.create', __('users.create'), [1, 2, 3], ['class' =&gt; 'btn']) !!}\n                     @endcan\n                 &lt;\/div&gt;\n             &lt;\/div&gt;\n\n      ----------- end diff -----------\n\n\nFound 1 of 1 files that can be fixed in 0.697 seconds, 38.00 MB memory used<\/code><\/pre>\n<h3>\u76f8\u5173\u94fe\u63a5<\/h3>\n<ul>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=4HM%2F%2BdDutKsdKt6bjqmXGg%3D%3D.V%2FgJBAxxkJYd4YnsQeHy15P2aeZaH2kdO%2FtqRjrWCMNKoo8sMJU5elb1lySWWrxg\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/PHP-CS-Fixer\/PHP-CS-Fixer<\/a><\/li>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=e%2BKiXQceaEpXCjRgRhkSQw%3D%3D.v6XAf9aLQoKoU4wh98kK4OsgJdrqq9jgMqaTh0qRu9TJXNzH0PfYx0kR4kFPJ9PC\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/shufo\/blade-formatter<\/a><\/li>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=%2FfRrlGp4BlUalocwerYCZw%3D%3D.5za9Vksuha0wigVO9uPikq2%2F92GO1ITXlS09eMd6WOcxCWCpEViipglk4u3AnDo2a8tVs9hxJHJP13uTSLBd11eqeUdoWb7kLDR%2FAmxeXnM%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/guanguans\/laravel-skeleton\/tree\/main\/app\/Support\/PhpCsFixer<\/a><\/li>\n<\/ul>\n<h3>\u539f\u6587\u8fde\u63a5<\/h3>\n<ul>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=s7KGX5ijVMEf2rrwIKAhjQ%3D%3D.5WUoorFuU34GlWLsPaYHqTwXi5FWldJTOkk7FoZz3%2Fwol%2BOvTdGYUHZRbYhZgE8XCqdz3utlwG%2FOh1uvHhaOGA%3D%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/guanguans\/guanguans.github.io\/issues\/59<\/a><\/li>\n<\/ul>\n<p><a href=\"https:\/\/segmentfault.com\/a\/1190000047087602\">https:\/\/segmentfault.com\/a\/1190000047087602<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>php-cs-fixer \u96c6\u6210 blade-formatter \u6765\u683c\u5f0f\u5316 blade \u6a21\u677f \u51c6\u5907\u5de5\u4f5c \u5b89\u88c5 P [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[4],"tags":[],"class_list":["post-10020928","post","type-post","status-publish","format-standard","hentry","category-share"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts\/10020928","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10020928"}],"version-history":[{"count":0,"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts\/10020928\/revisions"}],"wp:attachment":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10020928"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10020928"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10020928"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}