summaryrefslogtreecommitdiff
path: root/zend/eval.cpp
blob: 674d39d86b14fe55b0c69691b480091faad75abe (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
/**
 *  Eval.cpp
 *
 *  This file holds the implementation for the Php::eval() function
 * 
 *  @author andot <https://github.com/andot>
 */

/**
 *  Dependencies
 */
#include "includes.h"

/**
 *  Open PHP namespace
 */
namespace Php {

/**
 *  Evaluate a PHP string
 *  @param  phpCode     The PHP code to evaluate
 *  @return Value       The result of the evaluation
 */
Value eval(const std::string &phpCode) 
{
    // we need the tsrm_ls variable
    TSRMLS_FETCH();

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

    // the return va
    zval *retval = nullptr;
    MAKE_STD_ZVAL(retval);
    
    // evaluate the string
    if (zend_eval_stringl_ex((char *)phpCode.c_str(), (int32_t)phpCode.length(), retval, (char *)"", 1 TSRMLS_CC) != SUCCESS)
    {
        // Do we want to throw an exception here? The original author of this code
        // did, but there are some reasons not to:
        //
        //  1. the PHP eval() function also does not throw exceptions.
        //
        //  2. the zend_eval_string() function already triggers a 
        //     'PHP parse error' when an error occurs, which also has
        //     to be handled. If we also throw an exception here, the
        //     user will have to write two error checks: for the error
        //     and the exception.
        //
        // if we _do_ want to throw an exception, we will first have to
        // prevent the original zend_error to occur, and then turn it
        // into an exception. An exception would be nicer from a C++
        // point of view, but because of the extra complexity, we do not
        // this for now.
        return nullptr;
    }
    else
    {
        // 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);

        // wrap the return value in a value object
        Value result(retval);

        // the retval should now have two references: the value object and the
        // retval itselves, so we can remove one of it (the zval_ptr_dtor only
        // decrements refcount and won't destruct anything because there still 
        // is one reference left inside the Value object)
        zval_ptr_dtor(&retval);
        
        // done
        return result;
    }
}

/**
 *  Include a file
 *  @param  filename
 *  @return Value
 */
Value include(const std::string &filename)
{
    // we can simply execute a file
    return File(filename).execute();
}

/**
 *  Include a file only once
 *  @param  filename
 *  @return Value
 */
Value include_once(const std::string &filename)
{
    // we can simply execute a file
    return File(filename).execute();
}

/**
 *  Require a file
 *  This causes a fatal error if the file does not exist
 *  @param  filename
 *  @return Value
 */
Value require(const std::string &filename)
{
    // create the file
    File file(filename);
    
    // execute if it exists
    if (file.exists()) return file.execute();
    
    // trigger fatal error
    error << filename << " does not exist" << std::flush;
    
    // unreachable
    return nullptr;
}

/**
 *  Require a file only once
 *  This causes a fatal error if the file does not exist
 *  @param  filename
 *  @return Value
 */
Value require_once(const std::string &filename)
{
    // create the file
    File file(filename);
    
    // execute if it exists
    if (file.exists()) return file.once();
    
    // trigger fatal error
    error << filename << " does not exist" << std::flush;
    
    // unreachable
    return nullptr;
}


/**
 *  End of namespace
 */
}