summaryrefslogtreecommitdiff
path: root/zend/opcodes.h
blob: 4e780834191dfa7162ed7ec8d727f7a7c5ae6d4a (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
/**
 *  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_ptr = nullptr;
        
        // 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 restured when
        // the oldstate is destructed), so we can now safely overwrite all the settings
        EG(return_value_ptr_ptr) = &retval_ptr;
        EG(active_op_array) = _opcodes;
        EG(no_extensions) = 1;
        if (!EG(active_symbol_table)) zend_rebuild_symbol_table(TSRMLS_C);
        CG(interactive) = 0;
        
        // the current exception
        zval* oldException = EG(exception);

        // execute the code
        zend_execute(_opcodes 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
        if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception) TSRMLS_CC);

        // we're ready if there is no return value
        if (!retval_ptr) return nullptr;
        
        // wrap the return value
        Value result(retval_ptr);
        
        // destruct the zval (this function will decrement the reference counter,
        // and only destruct if there are no other references left)
        zval_ptr_dtor(&retval_ptr);
        
        // 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
 */
}