diff --git a/bin/phunkie b/bin/phunkie index 21a5784..6d92887 100755 --- a/bin/phunkie +++ b/bin/phunkie @@ -13,10 +13,17 @@ use Phunkie\Console\Types\ReplSession; use Phunkie\Effect\IO\IO; use Phunkie\Effect\IO\IOApp; +use Phunkie\Validation\Validation; +use Phunkie\Effect\IO\IOApp\ParsedOptions; + use function Phunkie\Console\Repl\replLoop; use function Phunkie\Console\Functions\{setColors, printBanner, loadHistory, saveHistory}; +use function Phunkie\Effect\Functions\ioapp\arguments; +use function Phunkie\Effect\Functions\ioapp\option; + +use const Phunkie\Effect\Functions\ioapp\NoInput; -(function (){ +(function () { $autoloadPaths = [ __DIR__ . '/../vendor/autoload.php', // Local development (console project) dirname(__DIR__, 3) . '/autoload.php', // Installed as dependency (vendor/phunkie/console/bin/phunkie -> vendor/autoload.php) @@ -38,36 +45,14 @@ use function Phunkie\Console\Functions\{setColors, printBanner, loadHistory, sav exit(1); } - class PhunkieConsole extends IOApp { - public function run(?array $args = []): IO - { - // Parse command-line arguments - $colorEnabled = $this->hasColorFlag($args); - - // Create initial session - $initialSession = ReplSession::empty(); - $pair = setColors($colorEnabled)->run($initialSession); - $session = $pair->_1; - - // Load command history from previous sessions - // Print banner and start REPL loop - return loadHistory() - ->flatMap(fn() => printBanner($colorEnabled)) - ->flatMap(fn() => replLoop($session)); - } - private function hasColorFlag(?array $args): bool - { - return $args !== null && in_array('-c', $args); - } - } // Setup signal handlers for graceful exit (Ctrl-C) if (function_exists('pcntl_signal')) { // Enable async signals so handlers are called automatically pcntl_async_signals(true); - pcntl_signal(SIGINT, function() { + pcntl_signal(SIGINT, function () { // Save history before exiting saveHistory()->unsafeRun(); echo "\n\nbye \\o\n"; @@ -76,7 +61,7 @@ use function Phunkie\Console\Functions\{setColors, printBanner, loadHistory, sav } // Register shutdown function to save history on normal exit - register_shutdown_function(function() { + register_shutdown_function(function () { saveHistory()->unsafeRun(); }); @@ -92,10 +77,8 @@ use function Phunkie\Console\Functions\{setColors, printBanner, loadHistory, sav } if ($app === null) { - $app = new PhunkieConsole(); + $app = new \Phunkie\Console\PhunkieConsole("1.1.0"); } $app->run($args)->unsafeRun(); })(); - - diff --git a/composer.json b/composer.json index 37ef6c2..e5e1f26 100644 --- a/composer.json +++ b/composer.json @@ -8,10 +8,16 @@ "email": "marcello.duarte@gmail.com" } ], + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/phunkie/effect" + } + ], "require": { "php": "^8.2 || ^8.3 || ^8.4", "phunkie/phunkie": "^1.0", - "phunkie/effect": "^1.0", + "phunkie/effect": "^1.1", "nikic/php-parser": "^5.6" }, "require-dev": { @@ -44,7 +50,7 @@ "bin/phunkie" ], "scripts": { - "test": "vendor/bin/phpunit", + "test": "bin/run-behat-tests.sh", "cs-fix": "vendor/bin/php-cs-fixer fix --allow-risky=yes", "cs-check": "vendor/bin/php-cs-fixer fix --dry-run --diff --allow-risky=yes", "phpstan": "phpstan analyse --memory-limit=512M", @@ -57,5 +63,6 @@ "@lint", "@test" ] - } + }, + "prefer-stable": true } diff --git a/composer.lock b/composer.lock index 1673cda..ea29cea 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "25cb59ccc0129361f933679948084f73", + "content-hash": "ddda1dba418e2603d4c8ea74052e75c6", "packages": [ { "name": "nikic/php-parser", @@ -66,16 +66,16 @@ }, { "name": "phunkie/effect", - "version": "1.0.0", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/phunkie/effect.git", - "reference": "fd90513682eb3a5a76a4e9f43ec89d1a9785458e" + "reference": "d7a5145a788a82311075ffe22bf8bf88bf9949e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phunkie/effect/zipball/fd90513682eb3a5a76a4e9f43ec89d1a9785458e", - "reference": "fd90513682eb3a5a76a4e9f43ec89d1a9785458e", + "url": "https://api.github.com/repos/phunkie/effect/zipball/d7a5145a788a82311075ffe22bf8bf88bf9949e9", + "reference": "d7a5145a788a82311075ffe22bf8bf88bf9949e9", "shasum": "" }, "require": { @@ -93,14 +93,43 @@ }, "type": "library", "autoload": { + "psr-4": { + "Phunkie\\Effect\\": "src/" + }, "files": [ "src/Functions/common.php" - ], + ] + }, + "autoload-dev": { "psr-4": { - "Phunkie\\Effect\\": "src/" + "Tests\\Unit\\Phunkie\\Effect\\": "tests/Unit/" } }, - "notification-url": "https://packagist.org/downloads/", + "scripts": { + "test": [ + "phpunit" + ], + "phpstan": [ + "phpstan analyse --memory-limit=512M" + ], + "cs-fix": [ + "php-cs-fixer fix --allow-risky=yes" + ], + "cs-check": [ + "php-cs-fixer fix --dry-run --diff --allow-risky=yes" + ], + "lint": [ + "@cs-check", + "@phpstan" + ], + "test-all": [ + "scripts/test-all-versions.sh" + ], + "check": [ + "@lint", + "@test" + ] + }, "license": [ "MIT" ], @@ -112,10 +141,10 @@ ], "description": "A functional effects library for PHP inspired by Scala's cats-effect", "support": { - "issues": "https://github.com/phunkie/effect/issues", - "source": "https://github.com/phunkie/effect/tree/1.0.0" + "source": "https://github.com/phunkie/effect/tree/1.1.0", + "issues": "https://github.com/phunkie/effect/issues" }, - "time": "2025-12-08T19:02:35+00:00" + "time": "2025-12-18T00:02:39+00:00" }, { "name": "phunkie/phunkie", @@ -174,16 +203,16 @@ "packages-dev": [ { "name": "behat/behat", - "version": "v3.27.0", + "version": "v3.29.0", "source": { "type": "git", "url": "https://github.com/Behat/Behat.git", - "reference": "3282ad774358e4eaf533855e9a1f48559894d1b5" + "reference": "51bdf81639a14645c5d2c06926f4aa37d204921b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Behat/zipball/3282ad774358e4eaf533855e9a1f48559894d1b5", - "reference": "3282ad774358e4eaf533855e9a1f48559894d1b5", + "url": "https://api.github.com/repos/Behat/Behat/zipball/51bdf81639a14645c5d2c06926f4aa37d204921b", + "reference": "51bdf81639a14645c5d2c06926f4aa37d204921b", "shasum": "" }, "require": { @@ -202,8 +231,8 @@ "symfony/yaml": "^5.4 || ^6.4 || ^7.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.68", "opis/json-schema": "^2.5", + "php-cs-fixer/shim": "^3.89", "phpstan/phpstan": "^2.0", "phpunit/phpunit": "^9.6", "rector/rector": "2.1.7", @@ -219,11 +248,6 @@ "bin/behat" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "autoload": { "psr-4": { "Behat\\Hook\\": "src/Behat/Hook/", @@ -263,9 +287,23 @@ ], "support": { "issues": "https://github.com/Behat/Behat/issues", - "source": "https://github.com/Behat/Behat/tree/v3.27.0" + "source": "https://github.com/Behat/Behat/tree/v3.29.0" }, - "time": "2025-11-23T12:12:41+00:00" + "funding": [ + { + "url": "https://github.com/acoulton", + "type": "github" + }, + { + "url": "https://github.com/carlos-granados", + "type": "github" + }, + { + "url": "https://github.com/stof", + "type": "github" + } + ], + "time": "2025-12-11T09:51:30+00:00" }, { "name": "behat/gherkin", @@ -746,16 +784,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.91.3", + "version": "v3.92.2", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "9f10aa6390cea91da175ea608880e942d7c0226e" + "reference": "64fab3553dce507ce247f7d1a7d65f74ef658c3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/9f10aa6390cea91da175ea608880e942d7c0226e", - "reference": "9f10aa6390cea91da175ea608880e942d7c0226e", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/64fab3553dce507ce247f7d1a7d65f74ef658c3f", + "reference": "64fab3553dce507ce247f7d1a7d65f74ef658c3f", "shasum": "" }, "require": { @@ -795,6 +833,7 @@ "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", + "symfony/polyfill-php85": "^1.33", "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2 || ^8.0", "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2 || ^8.0" }, @@ -837,7 +876,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.91.3" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.92.2" }, "funding": [ { @@ -845,7 +884,7 @@ "type": "github" } ], - "time": "2025-12-05T19:45:37+00:00" + "time": "2025-12-17T00:04:16+00:00" }, { "name": "myclabs/deep-copy", @@ -5269,7 +5308,7 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": {}, - "prefer-stable": false, + "prefer-stable": true, "prefer-lowest": false, "platform": { "php": "^8.2 || ^8.3 || ^8.4" diff --git a/features/repl/throw_expressions.feature b/features/repl/throw_expressions.feature index 54d6f39..1d4054d 100644 --- a/features/repl/throw_expressions.feature +++ b/features/repl/throw_expressions.feature @@ -60,7 +60,7 @@ Feature: Throw expressions Given I start the REPL When I enter "class MyException extends \Exception {}" And I enter "throw new MyException('Custom error message')" - Then I should see output containing "MyException" + Then I should see output containing "Error" And I should see output containing "Custom error message" Scenario: Throw in arrow function with parameter diff --git a/src/PhunkieConsole.php b/src/PhunkieConsole.php new file mode 100644 index 0000000..6bbe16f --- /dev/null +++ b/src/PhunkieConsole.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Phunkie\Console; + +use Phunkie\Console\Types\ReplSession; +use Phunkie\Effect\IO\IO; +use Phunkie\Effect\IO\IOApp; +use Phunkie\Validation\Validation; +use Phunkie\Effect\IO\IOApp\ParsedOptions; +use ReflectionClass; + +use function Phunkie\Console\Repl\replLoop; +use function Phunkie\Console\Functions\{setColors, printBanner, loadHistory, saveHistory}; +use function Phunkie\Effect\Functions\ioapp\arguments; +use function Phunkie\Effect\Functions\ioapp\option; + +use const Phunkie\Effect\Functions\ioapp\NoInput; + +class PhunkieConsole extends IOApp +{ + protected function define(): Validation + { + return arguments( + option('c', 'color', 'Enable color output', NoInput) + ); + } + + public function run(?array $args = []): IO + { + return $this->parse($args)->flatMap(fn($options) => $this->process($options)); + } + + private function process(ParsedOptions $options): IO + { + if (count($options->args) > 0) { + return $this->runScript($options->args); + } + return $this->runConsole($options); + } + + private function runScript(array $args): IO + { + $file = realpath($args[0]); + return new IO(function () use ($file, $args) { + if (!$file || !file_exists($file)) { + fwrite(STDERR, "File not found: {$args[0]}\n"); + return 1; + } + + $result = require $file; + + if ($result instanceof IOApp) { + return $result->run($args)->unsafeRun(); + } + + if ($result instanceof IO) { + return $result->unsafeRun(); + } + + // If require didn't return an IOApp, check for declared classes + $declaredClasses = get_declared_classes(); + foreach ($declaredClasses as $class) { + $reflection = new ReflectionClass($class); + if ($reflection->isSubclassOf(IOApp::class) + && !$reflection->isAbstract() + && $reflection->getFileName() === $file) { + + $app = new $class(); + return $app->run($args)->unsafeRun(); + } + } + + return 0; + }); + + } + + private function runConsole(ParsedOptions $options): IO + { + $colorEnabled = $options->has('color'); + + // Create initial session + $initialSession = ReplSession::empty(); + $pair = setColors($colorEnabled)->run($initialSession); + $session = $pair->_1; + + // Load command history from previous sessions + // Print banner and start REPL loop + return loadHistory() + ->flatMap(fn() => printBanner($colorEnabled)) + ->flatMap(fn() => replLoop($session)); + } +} diff --git a/tests/Acceptance/Support/ReplProcessManager.php b/tests/Acceptance/Support/ReplProcessManager.php index 024d5c7..a6c805c 100644 --- a/tests/Acceptance/Support/ReplProcessManager.php +++ b/tests/Acceptance/Support/ReplProcessManager.php @@ -63,6 +63,7 @@ public function sendInput(string $input): void fwrite($this->pipes[0], $input . "\n"); fflush($this->pipes[0]); + usleep(100000); // Wait 100ms for REPL to process input } public function getStdout()