Skip to content

constructing exceptions in rust #203

@azjezz

Description

@azjezz

I have been struggling trying to implement the following:

<?php

final class FooException extends Exception {
  public static function from_bar(string $bar): self {
    return new self("Bar: " . $bar);
  }
}

I tried the following:

#[derive(Debug)]
#[php_class]
#[extends(ce::exception)]
pub struct FooException {}

#[php_impl]
impl FooException {
    pub fn from_bar() -> Zval {
        let zval: Zval = ZendClassObject::new(FooException {}).into_zval(false).unwrap();

        zval
    }
}

this results in an error when calling FooException::from_bar() ( trying to call non-static method statically ).

i tried making from_bar() into a function, but this also didn't work, as non of the exception properties were initialized, the exception will always not have a backtrace, ( file being "" , and line being 0 )

I initially tried to create the object and call it's constructor:

this one works, but it's the same as before, backtrace is completely missing, while it works perfectly for "normal" classes, it fails for Exceptions, i believe there's a zend function that i should call on the exception object before/after constructing it, but i can't find it ( yet ) in the ZEND API.

use ext_php_rs::error::Result;
use ext_php_rs::ffi;
use ext_php_rs::types::Zval;
use ext_php_rs::zend::ClassEntry;

pub fn construct(class: &ClassEntry, arguments: &[Zval]) -> Result<Zval> {
    let len = arguments.len();
    let class_ptr = class as *const _ as *mut _;
    let constructor_ptr = class.constructor;

    let object = unsafe {
        let zend_object = ffi::zend_objects_new(class_ptr);

        ffi::object_properties_init(zend_object, class_ptr);

        ffi::zend_call_known_function(
            constructor_ptr,
            zend_object,
            class_ptr,
            std::ptr::null_mut(),
            len as _,
            arguments.as_ptr() as _,
            std::ptr::null_mut(),
        );

        zend_object
            .as_mut()
            .expect("error: failed to allocate memory for object")
    };

    let mut result = Zval::new();
    result.set_object(object);

    Ok(result)
}


pub extern "C" fn from_bar(ex: &mut ExecuteData, retval: &mut Zval) {
    if ex.parser().parse().is_err() {
        return;
    }

    let ce = /* exception class entry */;
    let c = construct(ce, &["bar!.".into_zval(false).unwrap()]).unwrap();

    *retval = c;
}

So the solution i found, is the following, which is extremely slow:

macro_rules! throw {
    ($ce:expr, $message:expr) => {
        let e: ::ext_php_rs::exception::PhpException =
            ::ext_php_rs::exception::PhpException::new($message.to_string(), 0, $ce);

        e.throw()
            .expect(&format!("Failed to throw exception: {}", $message));
    };
}

pub extern "C" fn from_bar(ex: &mut ExecuteData, retval: &mut Zval) {
    if ex.parser().parse().is_err() {
        return;
    }

    let ce = /* exception class entry */;
    throw!(ce, "bar!");
    let object = ExecutorGlobals::take_exception().unwrap();

    retval.set_object(object.into_raw());
}

any help would be appreciated :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions