Skip to content

fix: change how to parse arguments. #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ jobs:

- name: Run PHPStan
run: composer run-script phpstan

- name: Run php-variable-hard-usage
run: composer run-script php-variable-hard-usage
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
],
"phpstan": [
"vendor/bin/phpstan analyse"
],
"php-variable-hard-usage": [
"php bin/php-variable-hard-usage check --threshold=300 src/"
]
},
"bin": [
Expand Down
6 changes: 4 additions & 2 deletions src/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Smeghead\PhpVariableHardUsage;

use Smeghead\PhpVariableHardUsage\Option\CommandFactory;

final class Command
{
/**
Expand All @@ -12,8 +14,8 @@ final class Command
*/
public function run(array $argv): int
{
$factory = new CommandFactory();
$command = $factory->createCommand($argv);
$factory = new CommandFactory($argv);
$command = $factory->create();
return $command->execute();
}
}
81 changes: 0 additions & 81 deletions src/CommandFactory.php

This file was deleted.

223 changes: 223 additions & 0 deletions src/Option/CommandFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
<?php

declare(strict_types=1);

namespace Smeghead\PhpVariableHardUsage\Option;

use Smeghead\PhpVariableHardUsage\Command\CheckCommand;
use Smeghead\PhpVariableHardUsage\Command\CommandInterface;
use Smeghead\PhpVariableHardUsage\Command\HelpCommand;
use Smeghead\PhpVariableHardUsage\Command\ScopesCommand;
use Smeghead\PhpVariableHardUsage\Command\SingleCommand;
use Smeghead\PhpVariableHardUsage\Command\VersionCommand;

/**
* コマンドライン引数を解析し、適切なコマンドと引数を生成するクラス
*/
final class CommandFactory
{
/** @var array<string> */
private array $argv;

/**
* @param array<string> $argv コマンドライン引数
*/
public function __construct(array $argv)
{
$this->argv = $argv;
}

/**
* コマンドライン引数を解析し、コマンドと引数を返す
*/
public function create(): CommandInterface
{
// 引数がない場合はヘルプコマンド
if (count($this->argv) < 2) {
return new HelpCommand();
}

$command = $this->argv[1];

// ヘルプと バージョン表示は特別処理
if ($command === '--help') {
return new HelpCommand();
}

if ($command === '--version') {
return new VersionCommand();
}

// コマンドに応じた処理
switch ($command) {
case 'single':
return $this->parseSingleCommand();

case 'scopes':
return $this->parseScopesCommand();

case 'check':
return $this->parseCheckCommand();

default:
// 後方互換性のため、引数そのものをファイル名として解釈
return new SingleCommand($command);
}
}

/**
* 単一ファイルコマンドを解析
*/
private function parseSingleCommand(): CommandInterface
{
$args = array_slice($this->argv, 2);

if (empty($args)) {
return new HelpCommand();
}

return new SingleCommand($args[0]);
}

/**
* スコープコマンドを解析
*/
private function parseScopesCommand(): CommandInterface
{
$args = array_slice($this->argv, 2);

if (empty($args)) {
return new HelpCommand();
}

return new ScopesCommand($args);
}

/**
* チェックコマンドを解析
*/
private function parseCheckCommand(): CommandInterface
{
$args = array_slice($this->argv, 2);

if (empty($args)) {
return new HelpCommand();
}

$parsedArgs = $this->parseArguments($args);

if (empty($parsedArgs->paths)) {
return new HelpCommand();
}

$threshold = isset($parsedArgs->options['threshold']) ? intval($parsedArgs->options['threshold']) : null;

return new CheckCommand($parsedArgs->paths, $threshold);
}

/**
* コマンドライン引数を解析して、オプションとパスに分離する
*
* @param array<string> $args
* @return ParsedArguments
*/
private function parseArguments(array $args): ParsedArguments
{
$options = [];
$paths = [];

$i = 0;
while ($i < count($args)) {
$arg = $args[$i];

if ($this->isOptionWithValue($arg, '--threshold', $args, $i)) {
$options['threshold'] = (int)$args[$i + 1];
$i += 2;
} elseif ($this->isOptionWithInlineValue($arg, '--threshold=', $matches)) {
$options['threshold'] = (int)$matches[1];
$i++;
} elseif ($this->isOption($arg)) {
[$name, $value] = $this->parseOption($arg);
$options[$name] = $value;
$i++;
} else {
$paths[] = $arg;
$i++;
}
}

return new ParsedArguments($paths, $options);
}

/**
* 値を持つオプションかどうかを判定
*
* @param string $arg 現在の引数
* @param string $optionName オプション名
* @param array<string> $args 全引数
* @param int $index 現在の位置
* @return bool
*/
private function isOptionWithValue(string $arg, string $optionName, array $args, int $index): bool
{
return $arg === $optionName && isset($args[$index + 1]);
}

/**
* インライン値を持つオプションかどうかを判定
*
* @param string $arg 現在の引数
* @param string $prefix オプションのプレフィックス
* @param null &$matches 正規表現のマッチ結果を格納する変数
* @return bool
*/
private function isOptionWithInlineValue(string $arg, string $prefix, &$matches): bool
{
return preg_match('/^' . preg_quote($prefix, '/') . '(\d+)$/', $arg, $matches) === 1;
}

/**
* オプションかどうかを判定
*
* @param string $arg 現在の引数
* @return bool
*/
private function isOption(string $arg): bool
{
return strpos($arg, '--') === 0;
}

/**
* オプション文字列をパースして名前と値を取得
*
* @param string $option オプション文字列
* @return array{0: string, 1: string|bool} [オプション名, オプション値]
*/
private function parseOption(string $option): array
{
$optName = substr($option, 2);

if (strpos($optName, '=') !== false) {
[$name, $value] = explode('=', $optName, 2);
return [$name, $value];
}

return [$optName, true];
}
}

/**
* パース済みの引数を表すクラス
*/
final class ParsedArguments
{
/**
* @param array<string> $paths パスのリスト
* @param array<string, string|int|bool|null> $options オプションのマップ
*/
public function __construct(
public readonly array $paths,
public readonly array $options
) {
}
}
Loading