Skip to content
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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-version: ['8.2', '8.3', '8.4']
php-version: ['8.2', '8.3', '8.4', '8.5']
name: PHP ${{ matrix.php-version }}
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -50,7 +50,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-version: ['8.2', '8.3', '8.4']
php-version: ['8.2', '8.3', '8.4', '8.5']
steps:
- uses: actions/checkout@v4
- name: Setup PHP
Expand All @@ -69,7 +69,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-version: ['8.2', '8.3', '8.4']
php-version: ['8.2', '8.3', '8.4', '8.5']
steps:
- uses: actions/checkout@v4
- name: Unset local path repositories
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ A powerful, interactive REPL (Read-Eval-Print Loop) console for [Phunkie](https:

## Requirements

- PHP 8.2, 8.3, or 8.4
- PHP 8.2, 8.3, 8.4, or 8.5
- Composer

## Installation
Expand Down Expand Up @@ -169,7 +169,7 @@ Run version-specific tests:
The project maintains comprehensive test coverage:
- **Unit Tests**: Testing individual components with PHPUnit
- **Acceptance Tests**: End-to-end REPL functionality with Behat
- **Cross-version Testing**: Automated testing across PHP 8.2, 8.3, and 8.4
- **Cross-version Testing**: Automated testing across PHP 8.2, 8.3, 8.4, and 8.5

### Code Quality

Expand Down
48 changes: 30 additions & 18 deletions bin/run-behat-tests.sh
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
#!/bin/bash

# Run Behat tests with version-appropriate feature inclusions/exclusions
# Run Behat with the features the running PHP version can actually support.
#
# Features that depend on a specific PHP release live in a directory named after
# it, e.g. features/repl/php8.5/. Each such directory is skipped when the running
# PHP is older than the version it names, so adding features/repl/php8.6/ needs
# no change to this script.

set -e

PHP_VERSION=$(php -r 'echo PHP_VERSION_ID;')

if [ "$PHP_VERSION" -lt 80300 ]; then
# PHP 8.2: run only compatible features (everything except 8.3 and 8.4 subdirectories)
echo "Running tests for PHP 8.2 (excluding PHP 8.3+ and 8.4 features)"
excluded=()
for dir in features/*/php[0-9]*.[0-9]*; do
[ -d "$dir" ] || continue

# Run all features that are not in php8.3 or php8.4 subdirectories
find features/repl -type f -name "*.feature" ! -path "*/php8.3/*" ! -path "*/php8.4/*" -print0 | \
xargs -0 ./vendor/bin/behat --format=progress
version=${dir##*/php}
major=${version%%.*}
minor=${version##*.}
required=$((major * 10000 + minor * 100))

elif [ "$PHP_VERSION" -lt 80400 ]; then
# PHP 8.3: run compatible features (everything except 8.4 subdirectory)
echo "Running tests for PHP 8.3 (excluding PHP 8.4 features)"
if [ "$PHP_VERSION" -lt "$required" ]; then
excluded+=("$dir")
fi
done

# Run all features that are not in php8.4 subdirectory
find features/repl -type f -name "*.feature" ! -path "*/php8.4/*" -print0 | \
xargs -0 ./vendor/bin/behat --format=progress

else
# PHP 8.4+: run all tests
echo "Running all tests for PHP 8.4+"
./vendor/bin/behat --format=progress
if [ ${#excluded[@]} -eq 0 ]; then
echo "Running all features"
exec ./vendor/bin/behat --format=progress
fi

echo "Skipping features that need a newer PHP: ${excluded[*]}"

prune=()
for dir in "${excluded[@]}"; do
prune+=(! -path "$dir/*")
done

find features -type f -name "*.feature" "${prune[@]}" -print0 | \
xargs -0 ./vendor/bin/behat --format=progress
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
}
],
"require": {
"php": "^8.2 || ^8.3 || ^8.4",
"phunkie/phunkie": "^1.0",
"php": "^8.2 || ^8.3 || ^8.4 || ^8.5",
"phunkie/phunkie": "^1.3",
"phunkie/effect": "^1.1",
"nikic/php-parser": "^5.6"
},
Expand Down
30 changes: 15 additions & 15 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ composer global require phunkie/console

### System Requirements

- PHP 8.2, 8.3, or 8.4
- PHP 8.2, 8.3, 8.4, or 8.5
- Composer
- readline extension (usually included with PHP)

Expand Down
62 changes: 62 additions & 0 deletions features/repl/casts.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
Feature: Type casts
As a PHP developer
I want to use type casts in the REPL
So that I can convert values between types

Scenario: Casting a numeric string to int
Given I start the REPL
When I enter "(int) \"42\""
Then I should see output containing "Int = 42"

Scenario: Casting a float to int truncates
Given I start the REPL
When I enter "(int) 1.9"
Then I should see output containing "Int = 1"

Scenario: Casting a string to float
Given I start the REPL
When I enter "(float) \"3.5\""
Then I should see output containing "Float = 3.5"

Scenario: Casting an int to string
Given I start the REPL
When I enter "(string) 42"
Then I should see output containing "String = \"42\""

Scenario: Casting zero to bool
Given I start the REPL
When I enter "(bool) 0"
Then I should see output containing "Bool = false"

Scenario: Casting a non-empty string to bool
Given I start the REPL
When I enter "(bool) \"phunkie\""
Then I should see output containing "Bool = true"

Scenario: Casting a scalar to array
Given I start the REPL
When I enter "(array) \"a\""
Then I should see output containing "Array = [\"a\"]"

Scenario: Casting an array to object
Given I start the REPL
When I enter "$o = (object) [\"a\" => 1]"
And I enter "$o->a"
Then I should see output containing "Int = 1"

Scenario: Casting an object to array
Given I start the REPL
When I enter "$values = (array) (object) [\"a\" => 1]"
And I enter "$values[\"a\"]"
Then I should see output containing "Int = 1"

Scenario: Cast applies to a variable
Given I start the REPL
When I enter "$n = \"7\""
And I enter "(int) $n"
Then I should see output containing "Int = 7"

Scenario: Cast binds tighter than arithmetic
Given I start the REPL
When I enter "(int) \"3\" + 4"
Then I should see output containing "Int = 7"
20 changes: 14 additions & 6 deletions features/repl/first_class_callable.feature
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ Feature: First-Class Callable Syntax
And I enter "$f('hello')"
Then I should see output containing "Int = 5"

# Note: First-class callable syntax for user-defined functions is not yet supported
# User-defined functions are stored as AST nodes, not as callable PHP functions
# Supporting this would require eval'ing the function definition to create a real PHP function

Scenario: First-class callable from user-defined function - known limitation
Scenario: First-class callable from user-defined function
Given I start the REPL
When I enter the following code:
"""
Expand All @@ -22,7 +18,19 @@ Feature: First-Class Callable Syntax
}
"""
And I enter "$addFunc = add(...)"
Then I should see output containing "Error"
And I enter "$addFunc(2, 3)"
Then I should see output containing "Int = 5"

Scenario: First-class callable from user-defined function passed to array_map
Given I start the REPL
When I enter the following code:
"""
function double($n) {
return $n * 2;
}
"""
And I enter "array_map(double(...), [1, 2, 3])"
Then I should see output containing "Array = [2, 4, 6]"

Scenario: First-class callable from static method
Given I start the REPL
Expand Down
48 changes: 48 additions & 0 deletions features/repl/php8.5/array_first_last.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
Feature: array_first() and array_last() (PHP 8.5)
As a PHP developer
I want to read the first and last element of an array
So that I do not have to reach for reset() or array_key_last()

Scenario: array_first() on a list
Given I start the REPL
When I enter "array_first([3, 4, 5])"
Then I should see output containing "Int = 3"

Scenario: array_last() on a list
Given I start the REPL
When I enter "array_last([3, 4, 5])"
Then I should see output containing "Int = 5"

Scenario: array_first() on an empty array
Given I start the REPL
When I enter "array_first([])"
Then I should see output containing "Null = null"

Scenario: array_last() on an empty array
Given I start the REPL
When I enter "array_last([])"
Then I should see output containing "Null = null"

Scenario: array_first() ignores keys
Given I start the REPL
When I enter "$data = [\"b\" => \"beta\", \"a\" => \"alpha\"]"
And I enter "array_first($data)"
Then I should see output containing "String = \"beta\""

Scenario: array_last() ignores keys
Given I start the REPL
When I enter "$data = [\"b\" => \"beta\", \"a\" => \"alpha\"]"
And I enter "array_last($data)"
Then I should see output containing "String = \"alpha\""

Scenario: array_first() does not move the internal pointer
Given I start the REPL
When I enter "$numbers = [10, 20, 30]"
And I enter "array_first($numbers)"
And I enter "array_first($numbers)"
Then I should see output containing "Int = 10"

Scenario: Piping an array into array_last()
Given I start the REPL
When I enter "[1, 2, 3] |> array_last(...)"
Then I should see output containing "Int = 3"
Loading
Loading