Files
2023-03-28 15:32:31 -06:00

659 lines
17 KiB
C

/**
* YAML parser and emitter PHP extension
*
* Copyright (c) 2007 Ryusuke SEKIYAMA. All rights reserved.
* Copyright (c) 2009 Keynetics Inc. All rights reserved.
* Copyright (c) 2015 Bryan Davis and contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* @package php_yaml
* @author Ryusuke SEKIYAMA <rsky0711@gmail.com>
* @author Bryan Davis <bd808@bd808.com>
* @copyright 2007 Ryusuke SEKIYAMA
* @copyright 2009 Keynetics Inc
* @copyright 2015 Bryan Davis and contributors
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
#include "php_yaml.h"
#include "php_yaml_int.h"
/* {{{ local prototypes
*/
static int php_yaml_check_callbacks(HashTable *callbacks);
static PHP_MINIT_FUNCTION(yaml);
static PHP_MSHUTDOWN_FUNCTION(yaml);
static PHP_MINFO_FUNCTION(yaml);
static PHP_GINIT_FUNCTION(yaml);
static PHP_FUNCTION(yaml_parse);
static PHP_FUNCTION(yaml_parse_file);
static PHP_FUNCTION(yaml_parse_url);
static PHP_FUNCTION(yaml_emit);
static PHP_FUNCTION(yaml_emit_file);
/* }}} */
/* {{{ globals */
ZEND_DECLARE_MODULE_GLOBALS(yaml);
/* }}} */
/* {{{ ini entries */
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("yaml.decode_binary", "0", PHP_INI_ALL, OnUpdateBool,
decode_binary, zend_yaml_globals, yaml_globals)
STD_PHP_INI_ENTRY("yaml.decode_timestamp", "0", PHP_INI_ALL, OnUpdateLong,
decode_timestamp, zend_yaml_globals, yaml_globals)
STD_PHP_INI_ENTRY("yaml.decode_php", "0", PHP_INI_ALL, OnUpdateBool,
decode_php, zend_yaml_globals, yaml_globals)
STD_PHP_INI_ENTRY("yaml.output_canonical", "0", PHP_INI_ALL, OnUpdateBool,
output_canonical, zend_yaml_globals, yaml_globals)
STD_PHP_INI_ENTRY("yaml.output_indent", "2", PHP_INI_ALL, OnUpdateLong,
output_indent, zend_yaml_globals, yaml_globals)
STD_PHP_INI_ENTRY("yaml.output_width", "80", PHP_INI_ALL, OnUpdateLong,
output_width, zend_yaml_globals, yaml_globals)
PHP_INI_END()
/* }}} */
/* {{{ argument informations */
#ifdef ZEND_BEGIN_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_yaml_parse, ZEND_SEND_BY_VAL,
ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, input)
ZEND_ARG_INFO(0, pos)
ZEND_ARG_INFO(1, ndocs)
ZEND_ARG_ARRAY_INFO(0, callbacks, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_yaml_parse_file, ZEND_SEND_BY_VAL,
ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, filename)
ZEND_ARG_INFO(0, pos)
ZEND_ARG_INFO(1, ndocs)
ZEND_ARG_ARRAY_INFO(0, callbacks, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_yaml_parse_url, ZEND_SEND_BY_VAL,
ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, url)
ZEND_ARG_INFO(0, pos)
ZEND_ARG_INFO(1, ndocs)
ZEND_ARG_ARRAY_INFO(0, callbacks, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_yaml_emit, ZEND_SEND_BY_VAL,
ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, data)
ZEND_ARG_INFO(0, encoding)
ZEND_ARG_INFO(0, linebreak)
ZEND_ARG_ARRAY_INFO(0, callbacks, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_yaml_emit_file, ZEND_SEND_BY_VAL,
ZEND_RETURN_VALUE, 2)
ZEND_ARG_INFO(0, filename)
ZEND_ARG_INFO(0, data)
ZEND_ARG_INFO(0, encoding)
ZEND_ARG_INFO(0, linebreak)
ZEND_ARG_ARRAY_INFO(0, callbacks, 0)
ZEND_END_ARG_INFO()
#else
#define arginfo_yaml_parse third_arg_force_ref
#define arginfo_yaml_parse_file third_arg_force_ref
#define arginfo_yaml_parse_url third_arg_force_ref
#endif
static zend_function_entry yaml_functions[] = {
PHP_FE(yaml_parse, arginfo_yaml_parse)
PHP_FE(yaml_parse_file, arginfo_yaml_parse_file)
PHP_FE(yaml_parse_url, arginfo_yaml_parse_url)
PHP_FE(yaml_emit, arginfo_yaml_emit)
PHP_FE(yaml_emit_file, arginfo_yaml_emit_file)
PHP_FE_END
};
/* }}} */
/* {{{ cross-extension dependencies */
static zend_module_dep yaml_deps[] = {
ZEND_MOD_OPTIONAL("date") {NULL, NULL, NULL, 0}
};
/* }}} */
zend_module_entry yaml_module_entry = { /* {{{ */
STANDARD_MODULE_HEADER_EX,
NULL,
yaml_deps,
"yaml",
yaml_functions,
PHP_MINIT(yaml),
PHP_MSHUTDOWN(yaml),
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
PHP_MINFO(yaml),
PHP_YAML_VERSION,
PHP_MODULE_GLOBALS(yaml),
PHP_GINIT(yaml),
NULL,
NULL,
STANDARD_MODULE_PROPERTIES_EX
};
/* }}} */
#ifdef COMPILE_DL_YAML
ZEND_GET_MODULE(yaml)
#endif
/* {{{ PHP_MINIT_FUNCTION */
static PHP_MINIT_FUNCTION(yaml)
{
REGISTER_INI_ENTRIES();
/* node style constants */
REGISTER_LONG_CONSTANT("YAML_ANY_SCALAR_STYLE",
YAML_ANY_SCALAR_STYLE, CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("YAML_PLAIN_SCALAR_STYLE",
YAML_PLAIN_SCALAR_STYLE, CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("YAML_SINGLE_QUOTED_SCALAR_STYLE",
YAML_SINGLE_QUOTED_SCALAR_STYLE, CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("YAML_DOUBLE_QUOTED_SCALAR_STYLE",
YAML_DOUBLE_QUOTED_SCALAR_STYLE, CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("YAML_LITERAL_SCALAR_STYLE",
YAML_LITERAL_SCALAR_STYLE, CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("YAML_FOLDED_SCALAR_STYLE",
YAML_FOLDED_SCALAR_STYLE, CONST_PERSISTENT | CONST_CS);
/* tag constants */
REGISTER_STRING_CONSTANT("YAML_NULL_TAG", YAML_NULL_TAG,
CONST_PERSISTENT | CONST_CS);
REGISTER_STRING_CONSTANT("YAML_BOOL_TAG", YAML_BOOL_TAG,
CONST_PERSISTENT | CONST_CS);
REGISTER_STRING_CONSTANT("YAML_STR_TAG", YAML_STR_TAG,
CONST_PERSISTENT | CONST_CS);
REGISTER_STRING_CONSTANT("YAML_INT_TAG", YAML_INT_TAG,
CONST_PERSISTENT | CONST_CS);
REGISTER_STRING_CONSTANT("YAML_FLOAT_TAG", YAML_FLOAT_TAG,
CONST_PERSISTENT | CONST_CS);
REGISTER_STRING_CONSTANT("YAML_TIMESTAMP_TAG", YAML_TIMESTAMP_TAG,
CONST_PERSISTENT | CONST_CS);
REGISTER_STRING_CONSTANT("YAML_SEQ_TAG", YAML_SEQ_TAG,
CONST_PERSISTENT | CONST_CS);
REGISTER_STRING_CONSTANT("YAML_MAP_TAG", YAML_MAP_TAG,
CONST_PERSISTENT | CONST_CS);
REGISTER_STRING_CONSTANT("YAML_PHP_TAG", YAML_PHP_TAG,
CONST_PERSISTENT | CONST_CS);
REGISTER_STRING_CONSTANT("YAML_MERGE_TAG", YAML_MERGE_TAG,
CONST_PERSISTENT | CONST_CS);
REGISTER_STRING_CONSTANT("YAML_BINARY_TAG", YAML_BINARY_TAG,
CONST_PERSISTENT | CONST_CS);
/* encoding constants */
REGISTER_LONG_CONSTANT("YAML_ANY_ENCODING", YAML_ANY_ENCODING,
CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("YAML_UTF8_ENCODING", YAML_UTF8_ENCODING,
CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("YAML_UTF16LE_ENCODING", YAML_UTF16LE_ENCODING,
CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("YAML_UTF16BE_ENCODING", YAML_UTF16BE_ENCODING,
CONST_PERSISTENT | CONST_CS);
/* linebreak constants */
REGISTER_LONG_CONSTANT("YAML_ANY_BREAK", YAML_ANY_BREAK,
CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("YAML_CR_BREAK", YAML_CR_BREAK,
CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("YAML_LN_BREAK", YAML_LN_BREAK,
CONST_PERSISTENT | CONST_CS);
REGISTER_LONG_CONSTANT("YAML_CRLN_BREAK", YAML_CRLN_BREAK,
CONST_PERSISTENT | CONST_CS);
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MSHUTDOWN_FUNCTION */
static PHP_MSHUTDOWN_FUNCTION(yaml)
{
UNREGISTER_INI_ENTRIES();
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(yaml)
{
php_info_print_table_start();
php_info_print_table_row(2, "LibYAML Support", "enabled");
php_info_print_table_row(2, "Module Version", PHP_YAML_VERSION);
php_info_print_table_row(2, "LibYAML Version", yaml_get_version_string());
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
}
/* }}} */
/* {{{ PHP_GINIT_FUNCTION() */
static PHP_GINIT_FUNCTION(yaml)
{
yaml_globals->decode_binary = 0;
yaml_globals->decode_timestamp = 0;
yaml_globals->decode_php = 0;
yaml_globals->timestamp_decoder = NULL;
yaml_globals->output_canonical = 0;
yaml_globals->output_indent = 2;
yaml_globals->output_width = 80;
}
/* }}} */
/* {{{ php_yaml_check_callbacks()
* Validate user supplied callback array contents
*/
static int php_yaml_check_callbacks(HashTable *callbacks)
{
zval *entry;
zend_string *key;
ZEND_HASH_FOREACH_STR_KEY_VAL(callbacks, key, entry) {
if (key) {
zend_string *name;
if (!zend_is_callable(entry, 0, &name)) {
if (name != NULL) {
php_error_docref(NULL, E_WARNING,
"Callback for tag '%s', '%s' is not valid",
key->val, name->val);
zend_string_release(name);
} else {
php_error_docref(NULL, E_WARNING,
"Callback for tag '%s' is not valid", key->val);
}
return FAILURE;
} else {
zend_string_release(name);
}
if (zend_string_equals_literal(key, YAML_TIMESTAMP_TAG)) {
YAML_G(timestamp_decoder) = entry;
}
} else {
php_error_docref(NULL, E_NOTICE,
"Callback key should be a string");
}
} ZEND_HASH_FOREACH_END();
return SUCCESS;
}
/* }}} */
/* {{{ proto mixed yaml_parse(string input[, int pos[, int &ndocs[, array callbacks]]])
Takes a YAML encoded string and converts it to a PHP variable. */
PHP_FUNCTION(yaml_parse)
{
zend_string *input;
zend_long pos = 0;
zval *zndocs = { 0 };
zval *zcallbacks = { 0 };
parser_state_t state;
zval yaml;
zend_long ndocs = 0;
memset(&state, 0, sizeof(state));
state.have_event = 0;
state.callbacks = NULL;
YAML_G(timestamp_decoder) = NULL;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(),
"S|lz/a/", &input, &pos, &zndocs,
&zcallbacks)) {
return;
}
if (zcallbacks != NULL) {
state.callbacks = Z_ARRVAL_P(zcallbacks);
if (FAILURE == php_yaml_check_callbacks(state.callbacks)) {
RETURN_FALSE;
}
state.eval_func = eval_scalar_with_callbacks;
} else {
state.eval_func = eval_scalar;
}
yaml_parser_initialize(&state.parser);
yaml_parser_set_input_string(&state.parser, (unsigned char *)input->val, input->len);
if (pos < 0) {
php_yaml_read_all(&state, &ndocs, &yaml);
} else {
php_yaml_read_partial(&state, pos, &ndocs, &yaml);
}
yaml_parser_delete(&state.parser);
if (zndocs != NULL) {
/* copy document count to var user sent in */
zval_ptr_dtor(zndocs);
ZVAL_LONG(zndocs, ndocs);
}
if (Z_TYPE_P(&yaml) == IS_UNDEF) {
RETURN_FALSE;
}
RETURN_ZVAL(&yaml, 1, 1);
}
/* }}} yaml_parse */
/* {{{ proto mixed yaml_parse_file(string filename[, int pos[, int &ndocs[, array callbacks]]])
*/
PHP_FUNCTION(yaml_parse_file)
{
char *filename = { 0 };
size_t filename_len = 0;
zend_long pos = 0;
zval *zndocs = { 0 };
zval *zcallbacks = { 0 };
php_stream *stream = { 0 };
FILE *fp = { 0 };
parser_state_t state;
zval yaml;
zend_long ndocs = 0;
memset(&state, 0, sizeof(state));
state.have_event = 0;
state.callbacks = NULL;
YAML_G(timestamp_decoder) = NULL;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(),
"s|lz/a/", &filename, &filename_len, &pos, &zndocs,
&zcallbacks)) {
return;
}
if (zcallbacks != NULL) {
state.callbacks = Z_ARRVAL_P(zcallbacks);
if (FAILURE == php_yaml_check_callbacks(state.callbacks)) {
RETURN_FALSE;
}
state.eval_func = eval_scalar_with_callbacks;
} else {
state.eval_func = eval_scalar;
}
if (NULL == (stream = php_stream_open_wrapper(filename, "rb",
IGNORE_URL | REPORT_ERRORS | STREAM_WILL_CAST,
NULL))) {
RETURN_FALSE;
}
if (FAILURE == php_stream_cast(
stream, PHP_STREAM_AS_STDIO, (void **) &fp, 1)) {
php_stream_close(stream);
RETURN_FALSE;
}
yaml_parser_initialize(&state.parser);
yaml_parser_set_input_file(&state.parser, fp);
if (pos < 0) {
php_yaml_read_all(&state, &ndocs, &yaml);
} else {
php_yaml_read_partial(&state, pos, &ndocs, &yaml);
}
yaml_parser_delete(&state.parser);
php_stream_close(stream);
if (zndocs != NULL) {
/* copy document count to var user sent in */
zval_ptr_dtor(zndocs);
ZVAL_LONG(zndocs, ndocs);
}
if (Z_TYPE_P(&yaml) == IS_UNDEF) {
RETURN_FALSE;
}
RETURN_ZVAL(&yaml, 1, 1);
}
/* }}} yaml_parse_file */
/* {{{ proto mixed yaml_parse_url(string url[, int pos[, int &ndocs[, array callbacks]]])
*/
PHP_FUNCTION(yaml_parse_url)
{
char *url = { 0 };
size_t url_len = 0;
zend_long pos = 0;
zval *zndocs = { 0 };
zval *zcallbacks = { 0 };
php_stream *stream = { 0 };
zend_string *input;
parser_state_t state;
zval yaml;
zend_long ndocs = 0;
memset(&state, 0, sizeof(state));
state.have_event = 0;
state.callbacks = NULL;
YAML_G(timestamp_decoder) = NULL;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(),
"s|lza/", &url, &url_len, &pos, &zndocs, &zcallbacks)) {
return;
}
if (zcallbacks != NULL) {
state.callbacks = Z_ARRVAL_P(zcallbacks);
if (FAILURE == php_yaml_check_callbacks(state.callbacks)) {
RETURN_FALSE;
}
state.eval_func = eval_scalar_with_callbacks;
} else {
state.eval_func = eval_scalar;
}
if (NULL == (stream = php_stream_open_wrapper(url, "rb", REPORT_ERRORS, NULL))) {
RETURN_FALSE;
}
input = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0);
php_stream_close(stream);
if (input == NULL) {
RETURN_FALSE;
}
yaml_parser_initialize(&state.parser);
yaml_parser_set_input_string(&state.parser, (unsigned char *)ZSTR_VAL(input), ZSTR_LEN(input));
if (pos < 0) {
php_yaml_read_all(&state, &ndocs, &yaml);
} else {
php_yaml_read_partial(&state, pos, &ndocs, &yaml);
}
yaml_parser_delete(&state.parser);
zend_string_release(input);
if (zndocs != NULL) {
/* copy document count to var user sent in */
zval_dtor(zndocs);
ZVAL_LONG(zndocs, ndocs);
}
if (Z_TYPE_P(&yaml) == IS_UNDEF) {
RETURN_FALSE;
}
RETURN_ZVAL(&yaml, 1, 1);
}
/* }}} yaml_parse_url */
/* {{{ proto string yaml_emit(mixed data[, int encoding[, int linebreak[, array callbacks]]])
*/
PHP_FUNCTION(yaml_emit)
{
zval *data = { 0 };
zend_long encoding = YAML_ANY_ENCODING;
zend_long linebreak = YAML_ANY_BREAK;
zval *zcallbacks = { 0 };
HashTable *callbacks = { 0 };
yaml_emitter_t emitter = { 0 };
smart_string str = { 0 };
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "z/|lla/",
&data, &encoding, &linebreak, &zcallbacks)) {
return;
}
if (zcallbacks != NULL) {
callbacks = Z_ARRVAL_P(zcallbacks);
if (FAILURE == php_yaml_check_callbacks(callbacks)) {
RETURN_FALSE;
}
}
yaml_emitter_initialize(&emitter);
yaml_emitter_set_output(
&emitter, &php_yaml_write_to_buffer, (void *) &str);
yaml_emitter_set_encoding(&emitter, (yaml_encoding_t) encoding);
yaml_emitter_set_break(&emitter, (yaml_break_t) linebreak);
yaml_emitter_set_canonical(&emitter, YAML_G(output_canonical));
yaml_emitter_set_indent(&emitter, YAML_G(output_indent));
yaml_emitter_set_width(&emitter, YAML_G(output_width));
yaml_emitter_set_unicode(&emitter, YAML_ANY_ENCODING != encoding);
if (SUCCESS == php_yaml_write_impl(
&emitter, data, (yaml_encoding_t) encoding,
callbacks)) {
RETVAL_STRINGL(str.c, str.len);
} else {
RETVAL_FALSE;
}
yaml_emitter_delete(&emitter);
smart_string_free(&str);
}
/* }}} yaml_emit */
/* {{{ proto bool yaml_emit_file(string filename, mixed data[, int encoding[, int linebreak[, array callbacks]]])
*/
PHP_FUNCTION(yaml_emit_file)
{
zend_string *filename = { 0 };
php_stream *stream = { 0 };
FILE *fp = { 0 };
zval *data = { 0 };
zend_long encoding = YAML_ANY_ENCODING;
zend_long linebreak = YAML_ANY_BREAK;
zval *zcallbacks = { 0 };
HashTable *callbacks = { 0 };
yaml_emitter_t emitter = { 0 };
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "Sz/|lla/",
&filename, &data, &encoding, &linebreak, &zcallbacks)) {
return;
}
if (zcallbacks != NULL) {
callbacks = Z_ARRVAL_P(zcallbacks);
if (FAILURE == php_yaml_check_callbacks(callbacks)) {
RETURN_FALSE;
}
}
if (NULL == (stream = php_stream_open_wrapper(filename->val, "wb",
IGNORE_URL | REPORT_ERRORS | STREAM_WILL_CAST,
NULL))) {
RETURN_FALSE;
}
if (FAILURE == php_stream_cast(
stream, PHP_STREAM_AS_STDIO, (void **) &fp, 1)) {
php_stream_close(stream);
RETURN_FALSE;
}
yaml_emitter_initialize(&emitter);
yaml_emitter_set_output_file(&emitter, fp);
yaml_emitter_set_encoding(&emitter, (yaml_encoding_t) encoding);
yaml_emitter_set_break(&emitter, (yaml_break_t) linebreak);
yaml_emitter_set_canonical(&emitter, YAML_G(output_canonical));
yaml_emitter_set_indent(&emitter, YAML_G(output_indent));
yaml_emitter_set_width(&emitter, YAML_G(output_width));
yaml_emitter_set_unicode(&emitter, YAML_ANY_ENCODING != encoding);
RETVAL_BOOL((SUCCESS == php_yaml_write_impl(
&emitter, data, YAML_ANY_ENCODING, callbacks)));
yaml_emitter_delete(&emitter);
php_stream_close(stream);
}
/* }}} yaml_emit_file */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/