summaryrefslogtreecommitdiff
path: root/zend/callable.cpp
blob: cf0dd64fb09cba867379ff1459e03be57cc576fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/**
 *  Function.cpp
 *
 *  Implementation for the function class
 *
 *  @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
 *  @copyright 2013 Copernica BV
 */
#include "includes.h"

/**
 *  Set up namespace
 */
namespace Php {

/**
 *  Map function names to their implementation
 *
 *  This is braindead, there should be a way to get this information
 *  from the "This" zval in the execute_data, I just can't find it
 *  @todo   Find a better way for this
 *  @var    std::map<std::string, Callable*>
 */
static std::map<std::string, Callable*> callables;

/**
 *  Function that is called by the Zend engine every time that a function gets called
 *  @param  ht
 *  @param  return_value
 *  @param  return_value_ptr
 *  @param  this_ptr
 *  @param  return_value_used
 *  @param  tsrm_ls
 *  @return integer
 */
void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS)
{
    // find the function name
    const char *name = get_active_function_name(TSRMLS_C);

    // retrieve the callable from the map
    auto *callable = callables.find(name)->second;

    // check if sufficient parameters were passed (for some reason this check
    // is not done by Zend, so we do it here ourselves)
    if (ZEND_NUM_ARGS() < callable->_required)
    {
        // PHP itself only generates a warning when this happens, so we do the same too
        Php::warning << name << "() expects at least " << callable->_required << " parameters, " << ZEND_NUM_ARGS() << " given" << std::flush;

        // and we return null
        RETURN_NULL();
    }
    else
    {
        // construct parameters
        ParametersImpl params(getThis(), ZEND_NUM_ARGS() TSRMLS_CC);

        // the function could throw an exception
        try
        {
            // get the result
            Value result(callable->invoke(params));

            // return a full copy of the zval, and do not destruct it
            RETVAL_ZVAL(result._val, 1, 0);
        }
        catch (Exception &exception)
        {
            // process the exception
            process(exception TSRMLS_CC);
        }
    }
}

/**
 *  Fill a function entry
 *
 *  This method is called when the extension is registering itself, when the
 *  function or method introduces himself
 *
 *  @param  entry       Entry to be filled
 *  @param  classname   Optional class name
 *  @param  flags       Is this a public property?
 */
void Callable::initialize(zend_function_entry *entry, const char *classname, int flags) const
{
    // track the callable
    callables[_name] = const_cast<Callable*>(this);

    // fill the members of the entity, and hide a pointer to the current object in the name
    entry->fname = _name.data();
    entry->handler = &Callable::invoke;
    entry->arg_info = _argv.get();
    entry->num_args = _argc;
    entry->flags = flags;

    // we should fill the first argument as well
    initialize((zend_internal_function_info*)_argv.get(), classname);
}

/**
 *  Fill a function entry
 *  @param  info        Info to be filled
 *  @param  classname   Optional classname
 */
void Callable::initialize(zend_internal_function_info *info, const char *classname) const
{
    // store the classname
    info->class_name = classname;

    // number of required arguments, and the expected return type
    info->required_num_args = _required;
    info->type_hint = (unsigned char)_return;

    // we do not support return-by-reference
    info->return_reference = false;

    // since php 5.6 there are _allow_null and _is_variadic properties. It's
    // not exactly clear what they do (@todo find this out) so for now we set
    // them to false
    info->allow_null = false;
    info->_is_variadic = false;
}

/**
 *  End of namespace
 */
}