summaryrefslogtreecommitdiff
path: root/zend/opcodes.h
blob: 1901f1f980edc276f4a2119a9da2eeb372353e7c (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
/**
 *  Opcodes.h
 *
 *  Class represents a set of opcodes of a PHP script that can be executed. This
 *  is an internal file that you normally do not have to instantiate yourself.
 *  Better use the Php::Script of Php::File classes.
 *
 *  @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
 *  @copyright 2014 Copernica BV
 */

/**
 *  Forward declarations
 */
struct _zend_op_array;

/**
 *  Namespace
 */
namespace Php {

/**
 *  Class definition
 */
class Opcodes
{
public:
    /**
     *  Constructor
     *  @param  opcodes
     */
    Opcodes(struct _zend_op_array *opcodes TSRMLS_DC) : _opcodes(opcodes)
    {
#ifdef ZTS
        // copy tsrm_ls param
        this->tsrm_ls = tsrm_ls;
#endif
    }

    /**
     *  Destructor
     */
    virtual ~Opcodes()
    {
        // leap out if opcodes were not valid
        if (!_opcodes) return;

        // clean up opcodes
        destroy_op_array(_opcodes TSRMLS_CC);
        efree(_opcodes);
    }

    /**
     *  Are the opcodes valid?
     *  @return bool
     */
    bool valid() const
    {
        return _opcodes != nullptr;
    }

    /**
     *  Execute the opcodes
     *  @return Value
     */
    Value execute() const
    {
        // if the script could not be compiled, we return null
        if (!_opcodes) return nullptr;

        // pointer that is going to hold the return value of the script
        zval retval;

        // initialize to null
        ZVAL_NULL(&retval);

        // the zend engine is probably already busy processing opcodes, so we store
        // the current execute state before we're going to switch the runtime to
        // our own set of opcodes
        ExecuteState execState(0 TSRMLS_CC);

        // old execute state has been saved (and will automatically be restored when
        // the oldstate is destructed), so we can now safely overwrite all the settings
        CG(active_op_array) = _opcodes;
        EG(no_extensions) = 1;
        if (!EG(current_execute_data)->symbol_table) zend_rebuild_symbol_table(TSRMLS_C);

        // the current exception
        auto *oldException = EG(exception);

        // execute the code
        zend_execute(_opcodes, &retval TSRMLS_CC);

        // was an exception thrown inside the eval()'ed code? In that case we
        // throw a C++ new exception to give the C++ code the chance to catch it
        // todo: OrigException with constructor for zend_object
        // if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception) TSRMLS_CC);

        // we're ready if there is no return value
        if (ZVAL_IS_NULL(&retval)) return nullptr;

        // wrap the return value
        Value result(&retval);

        // copy the pointer into a value object, and return that
        return result;
    }

private:
    /**
     *  The opcodes
     *  @var zend_op_array
     */
    struct _zend_op_array *_opcodes;

#ifdef ZTS
    /**
     *  When in thread safety mode, we also keep track of the TSRM_LS var
     *  @var void***
     */
    void ***tsrm_ls;
#endif

};

/**
 *  End of namespace
 */
}