Skip to content
This repository has been archived by the owner on Apr 1, 2024. It is now read-only.

Commit

Permalink
Quick checkin
Browse files Browse the repository at this point in the history
Summary: I just want a checkin of this somewhere that's not my dev server.

Test Plan: None
  • Loading branch information
Marcel Laverdet committed Jun 22, 2009
0 parents commit 154d1d5
Show file tree
Hide file tree
Showing 9 changed files with 1,511 additions and 0 deletions.
13 changes: 13 additions & 0 deletions Makefile.xhp
@@ -0,0 +1,13 @@
all: xhp_scanner.lex.cpp xhp_parser.yacc.cpp

clean:
rm phpx xhp_scanner.lex.cpp xhp_scanner.lex.hpp xhp_parser.yacc.cpp xhp_parser.yacc.hpp xhp_parser.yacc.output

xhp_scanner.lex.cpp: xhp_scanner.l
flex --header-file=xhp_scanner.lex.hpp --prefix=xhp -o $@ -d $<

xhp_parser.yacc.cpp: xhp_parser.y
bison --debug --name-prefix=xhp --verbose -d -o $@ $<

phpx: xhp_scanner.lex.cpp xhp_parser.yacc.cpp parser.cpp lineno_str.cpp
g++ -ggdb -Wall $^ -o $@
80 changes: 80 additions & 0 deletions code_rope.cpp
@@ -0,0 +1,80 @@
#include "code_rope.hpp"
using namespace std;

code_rope::code_rope(const __gnu_cxx::rope<char> str, const size_t no /* = 0 */, const size_t lf /* = 0 */) : str(str), lf(lf), no(no) {}

code_rope::code_rope(const code_rope& str, const size_t no /* = 0 */, const size_t lf /* = 0 */) : str(str.str), lf(lf), no(no) {
if (str.lf || str.no) {
if (!no && !lf) {
this->lf = str.lf;
this->no = str.no;
} else {
throw new std::exception();
}
} else {
this->no = no;
this->lf = lf;
}
}

const char* code_rope::c_str() const {
if (this->no > 1) {
__gnu_cxx::rope<char> whitespace(this->no - 1, '\n');
whitespace += this->str;
return whitespace.c_str();
} else {
return this->str.c_str();
}
}

void code_rope::prepend(const char* str) {
this->str = __gnu_cxx::rope<char>(str) + this->str;
}

const char code_rope::back() const {
return this->str.empty() ? 0 : this->str.back();
}

void code_rope::pop_back() {
this->str.pop_back();
}

code_rope code_rope::operator+(const code_rope& right) const {
size_t diff;
size_t no, lf;
__gnu_cxx::rope<char> glue;
if (this->no && right.no) {
no = this->no;
if (right.no > this->no + this->lf) {
diff = right.no - this->no - this->lf;
lf = this->lf + right.lf + diff;
glue = __gnu_cxx::rope<char>(diff, '\n');
} else {
no = this->no;
lf = this->lf + right.lf;
}
} else if (right.no) {
no = right.no;
lf = this->lf + right.lf;
} else {
no = this->no;
lf = this->lf + right.lf;
}
return code_rope(this->str + glue + right.str, no, lf);
}

code_rope code_rope::operator+(const char* right) const {
return code_rope(this->str + right, this->no, this->lf);
}

code_rope& code_rope::operator=(const char* str) {
this->str = str;
this->no = this->lf = 0;
return *this;
}

code_rope operator+(const char* left, const code_rope& right) {
code_rope ret(right);
ret.prepend(left);
return ret;
}
21 changes: 21 additions & 0 deletions code_rope.hpp
@@ -0,0 +1,21 @@
#include <string>
#include <ext/rope>

class code_rope {
protected:
__gnu_cxx::rope<char> str;
size_t lf; /* how many line breaks this code contains */
size_t no; /* line number this code starts on */

public:
code_rope(const __gnu_cxx::rope<char> = "", const size_t = 0, const size_t = 0);
code_rope(const code_rope&, const size_t = 0, const size_t = 0);
const char* c_str() const;
void prepend(const char* str);
const char back() const;
void pop_back();
code_rope operator+(const code_rope& right) const;
code_rope operator+(const char*) const;
code_rope& operator=(const char*);
};
code_rope operator+(const char*, const code_rope&);
9 changes: 9 additions & 0 deletions config.m4
@@ -0,0 +1,9 @@
PHP_ARG_ENABLE(xhp, xhp,
[ --enable-xhp Enable XHP])

PHP_REQUIRE_CXX()
if test "$PHP_XHP" = "yes"; then
PHP_ADD_LIBRARY(stdc++,, XHP_SHARED_LIBADD)
PHP_SUBST(XHP_SHARED_LIBADD)
PHP_NEW_EXTENSION(xhp, xhp_scanner.lex.cpp xhp_parser.yacc.cpp code_rope.cpp ext.cpp, $ext_shared)
fi
208 changes: 208 additions & 0 deletions ext.cpp
@@ -0,0 +1,208 @@
#include "ext.hpp"
#include "xhp_parser.hpp"
#include "zend.h"
#include "zend_API.h"
#include "zend_compile.h"
#include "zend_hash.h"
#include "zend_extensions.h"
#include <string>

typedef zend_op_array* (zend_compile_file_t)(zend_file_handle*, int TSRMLS_DC);
typedef zend_op_array* (zend_compile_string_t)(zval*, char* TSRMLS_DC);
static zend_compile_file_t* dist_compile_file;
static zend_compile_string_t* dist_compile_string;

typedef struct {
const char* str;
size_t pos;
size_t len;
} xhp_stream_t;

size_t xhp_stream_reader(xhp_stream_t* handle, char* buf, size_t len TSRMLS_DC) {
if (len > handle->len - handle->pos) {
len = handle->len - handle->pos;
}
if (len) {
memcpy(buf, handle->str + handle->pos, len);
buf[len] = 0;
handle->pos += len;
return len;
} else {
return 0;
}
}

long xhp_stream_fteller(xhp_stream_t* handle TSRMLS_DC) {
return (long)handle->pos;
}

char* xhp_parse_str(const char* code, const char* filename, int firsttok) {

// Run it through the php superset parser which may generate valid php code...
void* scanner;
code_rope buf;
xhp_extra_type extra;
extra.firsttoken = firsttok;

// xhpdebug = 1;
xhplex_init(&scanner);
xhpset_extra(&extra, scanner);
xhp_scan_string(code, scanner);
int ret = xhpparse(scanner, filename, &buf);
xhplex_destroy(scanner);
if (ret) {
zend_error(E_COMPILE_ERROR, buf.c_str());
return NULL;
} else {
// Create a string stream with the rewritten code and give to compile_file
return estrdup(buf.c_str());
}
}

static zend_op_array* xhp_compile_file(zend_file_handle* f, int type TSRMLS_DC) {

if (open_file_for_scanning(f TSRMLS_CC) == FAILURE) {
// If opening the file fails just send it to the original func
return dist_compile_file(f, type TSRMLS_CC);
}

if (f->type == ZEND_HANDLE_STREAM && f->handle.stream.interactive) {
fprintf(stderr, "Warning: Using PHP + XHP in interactive mode will lead to undesirable behavior; execution will not commence until EOF (^D) is encountered.\n");
}

// Read full program from zend stream
std::string str;
char buf[4096];
size_t len;
while (len = zend_stream_read(f, (char*)&buf, 4095 TSRMLS_CC)) {
buf[len] = 0;
str += buf;
}

// Run this through xhp? Quick heuristic to determine if we can avoid a 2nd parse stage
bool maybe_xhp = false;
const char* cstr = str.c_str();
const char* ii;
for (ii = cstr; *ii; ++ii) {
if (*ii == '<') { // </a>
if (ii[1] == '/') {
maybe_xhp = 1;
break;
}
} else if (*ii == '/' && ii[1] == '>') { // <a />
maybe_xhp = 1;
break;
}
}

// If this file contains xhp run through the xhp parse stage
const char* rewrit;
if (maybe_xhp) {
bool old_in_comp = CG(in_compilation);
CG(in_compilation) = true;
rewrit = xhp_parse_str(cstr, f->filename ? f->filename : "", 0);
if (rewrit == NULL) {
zend_bailout();
}
CG(in_compilation) = old_in_comp;
len = strlen(rewrit);
} else {
rewrit = cstr;
len = ii - cstr;
}

// Create a fake stream
xhp_stream_t stream_data;
stream_data.str = rewrit;
stream_data.pos = 0;
stream_data.len = len;

zend_file_handle fake_file;
fake_file.type = ZEND_HANDLE_STREAM;
fake_file.opened_path = f->opened_path ? estrdup(f->opened_path) : NULL;
fake_file.filename = f->filename;
fake_file.free_filename = false;

fake_file.handle.stream.handle = &stream_data;
fake_file.handle.stream.reader = (zend_stream_reader_t)&xhp_stream_reader;
fake_file.handle.stream.closer = NULL;
fake_file.handle.stream.fteller = (zend_stream_fteller_t)&xhp_stream_fteller;
fake_file.handle.stream.interactive = 0;

zend_op_array* ret = dist_compile_file(&fake_file, type TSRMLS_CC);

if (maybe_xhp) {
efree(const_cast<char*>(rewrit));
}
return ret;
}

static zend_op_array* xhp_compile_string(zval* str, char *filename TSRMLS_DC) {

// Cast to str
zval tmp;
if (str->type != IS_STRING) {
tmp = *str;
zval_copy_ctor(&tmp);
convert_to_string(&tmp);
str = &tmp;
}

// Rewrite the string
char* rewrit = xhp_parse_str(str->value.str.val, "", t_PHP_FAKE_OPEN_TAG);
if (str == &tmp) {
zval_dtor(&tmp);
}
if (rewrit == NULL) {
return NULL;
}

// Create another tmp zval with the rewritten PHP code and pass it to the original function
INIT_ZVAL(tmp);
tmp.type = IS_STRING;
tmp.value.str.val = rewrit;
tmp.value.str.len = strlen(rewrit);
zend_op_array* ret = dist_compile_string(&tmp, filename TSRMLS_CC);
zval_dtor(&tmp);
return ret;
}

static PHP_MINIT_FUNCTION(xhp) {

// APC has this crazy magic api you can use to avoid the race condition for when an extension overwrites
// the compile_file function. The desired order here is APC -> xhp -> PHP, that way APC can cache the
// file as usual.
zend_module_entry *apc_lookup;
zend_constant *apc_magic;
if (zend_hash_find(&module_registry, "apc", sizeof("apc"), (void**)&apc_lookup) != FAILURE &&
zend_hash_find(EG(zend_constants), "\000apc_magic", 11, (void**)&apc_magic) != FAILURE) {
zend_compile_file_t* (*apc_set_compile_file)(zend_compile_file_t*) = (zend_compile_file_t* (*)(zend_compile_file_t*))apc_magic->value.value.lval;
dist_compile_file = apc_set_compile_file(NULL);
apc_set_compile_file(xhp_compile_file);
} else {
dist_compile_file = zend_compile_file;
zend_compile_file = xhp_compile_file;
}

// For eval
dist_compile_string = zend_compile_string;
zend_compile_string = xhp_compile_string;
return SUCCESS;
}

zend_module_entry xhp_module_entry = {
STANDARD_MODULE_HEADER,
PHP_XHP_EXTNAME,
NULL,
PHP_MINIT(xhp),
NULL,
NULL,
NULL,
NULL,
PHP_XHP_VERSION,
STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_XHP
ZEND_GET_MODULE(xhp)
#endif
11 changes: 11 additions & 0 deletions ext.hpp
@@ -0,0 +1,11 @@
#pragma once
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include "php.h"

#define PHP_XHP_VERSION "1.0"
#define PHP_XHP_EXTNAME "xhp"

extern zend_module_entry xhp_module_entry;
#define phpext_xhp &xhp_module_entry
46 changes: 46 additions & 0 deletions xhp_parser.hpp
@@ -0,0 +1,46 @@
#pragma once

#include <stdio.h>
#include "code_rope.hpp"

#define YYSTYPE code_rope

#define YY_HEADER_EXPORT_START_CONDITIONS

typedef struct {
int firsttoken;
char* heredoc_eom;
size_t heredoc_eom_len;
char* heredoc_data_start;
char* heredoc_data_last;
} xhp_extra_type;
#define YY_EXTRA_TYPE xhp_extra_type*

#define YYLTYPE_IS_DECLARED
typedef struct YYLTYPE {
int internal_line;
int actual_line_offset;
int first_line;
int first_column;
int last_line;
int last_column;
} YYLTYPE;


void flexBEGIN(int, void*);
void flex_push_state(int, void*);
void flex_pop_state(void*);

#include "xhp_parser.yacc.hpp"
#ifndef FLEX_SCANNER
// You can't include flex's header from within flex or shit goes to hell
#include "xhp_scanner.lex.hpp"
#define yy_push_state(a) flex_push_state(a, yyscanner);
#define yy_pop_state() flex_pop_state(yyscanner);
#endif

extern int xhpdebug;
int xhpparse(void*, const char*, code_rope*);
#ifndef FLEX_SCANNER
void* xhp_scan_string(const char *yy_str, void* yyscanner);
#endif

0 comments on commit 154d1d5

Please sign in to comment.