Skip to content
Open
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
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI

on:
push:
branches:
- main
pull_request:

jobs:
test:
name: "Build and test"
runs-on: ubuntu-latest
continue-on-error: false
strategy:
fail-fast: false
matrix:
php: ['8.3', '8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: phpize

- name: Set up
run: phpize

- name: Configure
run: ./configure --enable-custom_cast

- name: Make and install
run: sudo make -j"$(nproc)" install

- name: Run tests
run: >-
REPORT_EXIT_STATUS=1
NO_INTERACTION=1
TEST_PHP_EXECUTABLE=$(which php)
php run-tests.php -n --show-diff tests
32 changes: 22 additions & 10 deletions custom_cast.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,25 +267,22 @@ static zend_result custom_cast_do_cast(
/**
* Ensure that the flags for a class entry correspond to a userland class
*/
static void require_user_class(uint32_t flags) {
static const char* require_user_class(uint32_t flags) {
// Must actually be on a class
if (flags & ZEND_ACC_ENUM) {
zend_error_noreturn(E_ERROR, "Cannot apply #[CustomCasting\\CustomCastable] to enum");
return "Cannot apply #[CustomCasting\\CustomCastable] to enum";
}
if (flags & ZEND_ACC_INTERFACE) {
zend_error_noreturn(E_ERROR, "Cannot apply #[CustomCasting\\CustomCastable] to interface");
return "Cannot apply #[CustomCasting\\CustomCastable] to interface";
}
if (flags & ZEND_ACC_TRAIT) {
zend_error_noreturn(E_ERROR, "Cannot apply #[CustomCasting\\CustomCastable] to trait");
return "Cannot apply #[CustomCasting\\CustomCastable] to trait";
}
// Use ce->type != ZEND_INTERNAL_CLASS
if (flags & ZEND_ACC_LINKED) {
zend_error_noreturn(
E_ERROR,
"#[CustomCasting\\CustomCastable] is for user classes, internal classes can set a custom cast handler"
);
return "#[CustomCasting\\CustomCastable] is for user classes, internal classes can set a custom cast handler";
}

return NULL;
}

/**
Expand Down Expand Up @@ -378,12 +375,27 @@ static void ensure_class_has_interface(zend_class_entry *scope) {
scope->interface_names = newInterfaceSet;
}

#if PHP_VERSION_ID < 80500
static void validate_custom_castable(
zend_attribute *attr, uint32_t target, zend_class_entry *scope)
#else
static zend_string *validate_custom_castable(
zend_attribute *attr, uint32_t target, zend_class_entry *scope)
#endif
{
require_user_class(scope->ce_flags);
const char *error = require_user_class(scope->ce_flags);
if (error != NULL) {
#if PHP_VERSION_ID < 80500
zend_error_noreturn(E_ERROR, error);
#else
return zend_string_init(error, strlen(error), 0);
#endif
}
ensure_class_has_interface(scope);
scope->default_object_handlers = &custom_cast_obj_handlers;
#if PHP_VERSION_ID >= 80500
return NULL;
#endif
}

static void setup_CustomCastable_as_attribute(zend_class_entry *class_entry) {
Expand Down
2 changes: 1 addition & 1 deletion tests/attribute_on_wrong/attribute_on_class_missing.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ class Demo {

?>
--EXPECTF--
Fatal error: Class Demo contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (CustomCasting\HasCustomCast::__doCast) in %s on line %d
Fatal error: Class Demo contains 1 abstract method and must therefore be declared abstract or implement the remaining method%rs?%r (CustomCasting\HasCustomCast::__doCast) in %s on line %d