let inner_obj = self.inner;
let cached_name = self.cached_name.clone();
{% for param_name in string_params %}
let {{ param_name }} = {{ param_name }}.to_string();
{% endfor %}

// SAFETY: PHP objects are single-threaded within a request.
// Use block_in_place if already in a tokio context; otherwise use block_on.
if let Ok(handle) = tokio::runtime::Handle::try_current() {
    tokio::task::block_in_place(|| {
        handle.block_on(async {
            match unsafe { (*inner_obj).try_call_method("{{ method_name }}", {{ args_expr }}) } {
{% if is_result_type %}
                Ok(val) => {
{%- if native_return_binding %}
                    if let Some(native) = <&{{ native_return_binding }} as ext_php_rs::convert::FromZval>::from_zval(&val) {
                        Ok(native.clone().into())
                    } else {
                        let json_str = val.string().unwrap_or_default();
                        serde_json::from_str(&json_str).map_err(|e| {{ deserialize_error_expr }})
                    }
{%- else %}
                    let json_str = val.string().unwrap_or_default();
                    serde_json::from_str(&json_str).map_err(|e| {{ deserialize_error_expr }})
{%- endif %}
                }
{% else %}
                Ok(val) => val.string().unwrap_or_default().parse().unwrap_or_default(),
{% endif %}
                Err(e) => Err({{ call_error_expr }}),
            }
        })
    })
} else {
    WORKER_RUNTIME.block_on(async {
        match unsafe { (*inner_obj).try_call_method("{{ method_name }}", {{ args_expr }}) } {
{% if is_result_type %}
            Ok(val) => {
{%- if native_return_binding %}
                if let Some(native) = <&{{ native_return_binding }} as ext_php_rs::convert::FromZval>::from_zval(&val) {
                    Ok(native.clone().into())
                } else {
                    let json_str = val.string().unwrap_or_default();
                    serde_json::from_str(&json_str).map_err(|e| {{ deserialize_error_expr }})
                }
{%- else %}
                let json_str = val.string().unwrap_or_default();
                serde_json::from_str(&json_str).map_err(|e| {{ deserialize_error_expr }})
{%- endif %}
            }
{% else %}
            Ok(val) => val.string().unwrap_or_default().parse().unwrap_or_default(),
{% endif %}
            Err(e) => Err({{ call_error_expr }}),
        }
    })
}
