summaryrefslogtreecommitdiff
path: root/zend/callable.h
blob: e875d0a031a9c52c67bc00847ceb137bf7fd712f (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/**
 *  Callable.h
 *
 *  Object represents a callable function or method that is defined with the CPP
 *  API.
 *
 *  @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
 *  @copyright 2013 Copernica BV
 */

/**
 *  Dependencies
 */
#include <cstring>

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

/**
 *  Class definition
 */
class Callable
{
public:
    /**
     *  Constructor
     *  @param  name        Function or method name
     *  @param  arguments   Information about the arguments
     */
    Callable(const char *name, const Arguments &arguments = {}) :
        _name(name),
        _argc(arguments.size()),
        _argv(new zend_internal_arg_info[_argc + 1])
    {
        // the first record is initialized with information about the function,
        // so we skip that here
        int i=1;

        // loop through the arguments
        for (auto &argument : arguments)
        {
            // increment counter with number of required parameters
            if (argument.required()) _required++;

            // fill the arg info
            fill(&_argv[i++], argument);
        }
    }

    /**
     *  Copy constructor
     *  @param  that
     */
    Callable(const Callable &that) :
        _name(that._name),
        _return(that._return),
        _required(that._required),
        _argc(that._argc),
        _argv(nullptr) {}
        // @todo: we have no arguments after copy? is this correct?
        // we do have the argument count though...

    /**
     *  Move constructor
     *  @param  that
     */
    Callable(Callable &&that) :
        _name(std::move(that._name)),
        _return(that._return),
        _required(that._required),
        _argc(that._argc),
        _argv(std::move(that._argv)) {}

    /**
     *  Destructor
     */
    virtual ~Callable() = default;

    /**
     *  Method that gets called every time the function is executed
     *  @param  params      The parameters that were passed
     *  @return Variable    Return value
     */
    virtual Value invoke(Parameters &params) = 0;

    /**
     *  Fill a function entry
     *  @param  entry       Entry to be filled
     *  @param  ns          Active namespace
     *  @param  classname   Optional class name
     *  @param  flags       Access flags
     */
    void initialize(zend_function_entry *entry, const char *classname = nullptr, int flags = 0) const;

    /**
     *  Fill function info
     *  @param  info        Info object to be filled
     *  @param  ns          Active namespace
     *  @param  classname   Optional class name
     */
    void initialize(zend_internal_function_info *info, const char *classname = nullptr) const;


protected:
    /**
     *  Name of the function
     *  @var    std::string
     */
    std::string _name;

    /**
     *  Suggestion for the return type
     *  @var    Type
     */
    Type _return = Type::Undefined;

    /**
     *  Required number of arguments
     *  @var    unsigned integer
     */
    unsigned int _required = 0;

    /**
     *  Total number of arguments
     *  @var    integer
     */
    int _argc = 0;

    /**
     *  The arguments
     *  @var std::unique_ptr<zend_internal_arg_info[]>
     */
    std::unique_ptr<zend_internal_arg_info[]> _argv;

    /**
     *  Private helper method to fill an argument object
     *  @param  info        object from the zend engine
     *  @param  arg         original object
     */
    void fill(zend_internal_arg_info *info, const Argument &arg) const
    {
        // fill members
        info->name = arg.name();

        // are we filling an object
        if (arg.type() == Type::Object) info->class_name = arg.classname();
        else info->class_name = nullptr;

        // set the correct type-hint
        switch (arg.type())
        {
            case Type::Undefined:   info->type_hint = IS_UNDEF;     break;  // undefined means we'll accept any type
            case Type::Null:        info->type_hint = IS_UNDEF;     break;  // this is likely an error, what good would accepting NULL be? accept anything
            case Type::False:       info->type_hint = _IS_BOOL;     break;  // accept true as well ;)
            case Type::True:        info->type_hint = _IS_BOOL;     break;  // accept false as well
            case Type::Bool:        info->type_hint = _IS_BOOL;     break;  // any bool will do, true, false, the options are limitless
            case Type::Numeric:     info->type_hint = IS_LONG;      break;  // accept integers here
            case Type::Float:       info->type_hint = IS_DOUBLE;    break;  // floating-point values welcome too
            case Type::String:      info->type_hint = IS_STRING;    break;  // accept strings, should auto-cast objects with __toString as well
            case Type::Array:       info->type_hint = IS_ARRAY;     break;  // array of anything (individual members cannot be restricted)
            case Type::Object:      info->type_hint = IS_OBJECT;    break;  // must be an object of the given classname
            case Type::Callable:    info->type_hint = IS_CALLABLE;  break;  // anything that can be invoked
            default:                info->type_hint = IS_UNDEF;     break;  // if not specified we allow anything

        }

        // from PHP 5.6 and onwards, an is_variadic property can be set, this
        // specifies whether this argument is the first argument that specifies
        // the type for a variable length list of arguments. For now we only
        // support methods and functions with a fixed number of arguments.
        info->is_variadic = false;

        // this parameter is a regular type
        info->allow_null = arg.allowNull();
        info->pass_by_reference = arg.byReference();
    }

    /**
     *  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
     */
    static void invoke(INTERNAL_FUNCTION_PARAMETERS);

};

/**
 *  End of namespace
 */
}