├── EXPERIMENTAL ├── CREDITS ├── yajl ├── yajl_version.c ├── api │ ├── yajl_version.h │ ├── yajl_common.h │ ├── yajl_gen.h │ ├── yajl_tree.h │ └── yajl_parse.h ├── LICENSE ├── yajl_alloc.h ├── yajl_encode.h ├── yajl_alloc.c ├── yajl_buf.h ├── yajl_bytestack.h ├── yajl_parser.h ├── yajl_buf.c ├── yajl_lex.h ├── yajl.c ├── yajl_encode.c ├── ChangeLog ├── yajl_gen.c └── yajl_tree.c ├── .travis.yml ├── config.w32 ├── composer.json ├── jsonrpc.php ├── tests ├── 002-jsonrpc-client.phpt ├── 003-jsonrpc-server.phpt ├── 001.phpt ├── 005-jsonrpc-client-connect.phpt └── 004-jsonrpc-server-register.phpt ├── examples ├── swoole-jsonrpc-server.php ├── jsonrpc-client.php └── curl-epoll.c ├── jsr_utils.h ├── jsr_epoll.h ├── jsr_server.h ├── jsr_list.h ├── jsr_utils.c ├── jsr_epoll.c ├── config.m4 ├── php_jsonrpc.h ├── jsr_client.h ├── jsr_yajl.h ├── jsr_curl.h ├── jsr_list.c ├── jsonrpc.c ├── README-cn.md ├── README.md ├── jsr_curl.c └── jsr_yajl.c /EXPERIMENTAL: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | jsonrpc -------------------------------------------------------------------------------- /yajl/yajl_version.c: -------------------------------------------------------------------------------- 1 | #include "api/yajl_version.h" 2 | 3 | int yajl_version(void) 4 | { 5 | return YAJL_VERSION; 6 | } 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 5.3 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | 8 | notifications: 9 | email: false 10 | 11 | env: 12 | - REPORT_EXIT_STATUS=1 NO_INTERACTION=1 13 | 14 | before_script: phpize && ./configure && make 15 | 16 | script: make test 17 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | // If your extension references something external, use ARG_WITH 5 | // ARG_WITH("jsonrpc", "for jsonrpc support", "no"); 6 | 7 | // Otherwise, use ARG_ENABLE 8 | // ARG_ENABLE("jsonrpc", "enable jsonrpc support", "no"); 9 | 10 | if (PHP_JSONRPC != "no") { 11 | EXTENSION("jsonrpc", "jsonrpc.c"); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rryqszq4/jsonrpc", 3 | "description": "Lightweight, fast JsonRPC 2.0 client/server of PHP extension,base on multi_curl + epoll of client. ", 4 | "license": "PHP license 3.01", 5 | "authors": [ 6 | { 7 | "name": "rryqszq4", 8 | "email": "memwared@gmail.com" 9 | } 10 | ], 11 | "require": { 12 | "php": ">=5.3, <=5.6" 13 | }, 14 | "version": "0.0.9" 15 | } -------------------------------------------------------------------------------- /yajl/api/yajl_version.h: -------------------------------------------------------------------------------- 1 | #ifndef YAJL_VERSION_H_ 2 | #define YAJL_VERSION_H_ 3 | 4 | #include "yajl_common.h" 5 | 6 | #define YAJL_MAJOR 2 7 | #define YAJL_MINOR 1 8 | #define YAJL_MICRO 0 9 | 10 | #define YAJL_VERSION ((YAJL_MAJOR * 10000) + (YAJL_MINOR * 100) + YAJL_MICRO) 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | extern int YAJL_API yajl_version(void); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | 22 | #endif /* YAJL_VERSION_H_ */ 23 | 24 | -------------------------------------------------------------------------------- /jsonrpc.php: -------------------------------------------------------------------------------- 1 | "; 3 | 4 | if(!extension_loaded('jsonrpc')) { 5 | dl('jsonrpc.' . PHP_SHLIB_SUFFIX); 6 | } 7 | $module = 'jsonrpc'; 8 | $functions = get_extension_funcs($module); 9 | echo "Functions available in the test extension:$br\n"; 10 | foreach($functions as $func) { 11 | echo $func."$br\n"; 12 | } 13 | echo "$br\n"; 14 | $function = 'confirm_' . $module . '_compiled'; 15 | if (extension_loaded($module)) { 16 | $str = $function($module); 17 | } else { 18 | $str = "Module $module is not compiled into PHP"; 19 | } 20 | echo "$str\n"; 21 | ?> 22 | -------------------------------------------------------------------------------- /tests/002-jsonrpc-client.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for jsonrpc presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | call('http://www.baidu.com', "addition", array(3,5), "id_001"); 9 | $client->call('http://www.baidu.com', "addition", array(3,5), "id_002"); 10 | $client->call('http://www.baidu.com', "addition", array(3,5), "id_003"); 11 | $result = $client->execute(); 12 | echo $result[0]['id']; 13 | echo $result[1]['id']; 14 | echo $result[2]['id']; 15 | ?> 16 | --EXPECT-- 17 | id_001id_002id_003 -------------------------------------------------------------------------------- /tests/003-jsonrpc-server.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for jsonrpc presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | register('addition', function ($a, $b) { 12 | return $a + $b; 13 | }); 14 | 15 | // Return the response to the client 16 | echo $server->execute(); 17 | 18 | ?> 19 | --EXPECT-- 20 | { 21 | "jsonrpc": "2.0", 22 | "error": { 23 | "code": -32700, 24 | "message": "Parse error" 25 | }, 26 | "id": null 27 | } -------------------------------------------------------------------------------- /tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for jsonrpc presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECT-- 21 | jsonrpc extension is available 22 | -------------------------------------------------------------------------------- /examples/swoole-jsonrpc-server.php: -------------------------------------------------------------------------------- 1 | on('Request', function($request, $response){ 10 | if ($request->server['request_uri'] == "/jsonrpc_server"){ 11 | $payload = $request->rawContent(); 12 | $jsr_server = new Jsonrpc_Server($payload); 13 | $jsr_server->register('addition', 'add'); 14 | $res = $jsr_server->execute(); 15 | $response->end($res); 16 | unset($payload); 17 | unset($jsr_server); 18 | unset($res); 19 | }else { 20 | $response->end("error"); 21 | } 22 | }); 23 | $http->start(); 24 | ?> -------------------------------------------------------------------------------- /yajl/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007-2014, Lloyd Hilaiel 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /tests/005-jsonrpc-client-connect.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for jsonrpc presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | connect('https://github.com/rryqszq4/404'); 10 | $client->addition(3,5); 11 | $client->addition1(3,5); 12 | $result = $client->execute(); 13 | echo "response_total:".$client->response_total."\n"; 14 | echo "error_code:".$result[0]["error"]["code"]."\n"; 15 | echo "error_code:".$result[1]["error"]["code"]."\n"; 16 | 17 | $client->connect('https://github.com/rryqszq4/404'); 18 | $client->addition(3,5); 19 | $client->addition1(3,5); 20 | $result = $client->execute(); 21 | echo "response_total:".$client->response_total."\n"; 22 | echo "error_code:".$result[2]["error"]["code"]."\n"; 23 | echo "error_code:".$result[3]["error"]["code"]."\n"; 24 | ?> 25 | --EXPECT-- 26 | response_total:2 27 | error_code:-32404 28 | error_code:-32404 29 | response_total:4 30 | error_code:-32404 31 | error_code:-32404 32 | -------------------------------------------------------------------------------- /tests/004-jsonrpc-server-register.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for jsonrpc presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | "2.0", 9 | "id"=>123456, 10 | "method"=>"addition", 11 | "params"=>array(2,3) 12 | ); 13 | $server = new Jsonrpc_Server(json_encode($payload)); 14 | 15 | 16 | $server->register('addition', function ($a, $b) { 17 | return $a + $b; 18 | }); 19 | 20 | 21 | 22 | $add = function ($a, $b){ 23 | return $a + $b; 24 | }; 25 | $server->register('addition', $add); 26 | 27 | 28 | 29 | function add ($a, $b){ 30 | return $a + $b; 31 | } 32 | $server->register('addition', 'add'); 33 | 34 | 35 | class A 36 | { 37 | static public function add($a, $b) 38 | { 39 | return $a + $b; 40 | } 41 | } 42 | $server->register('addition', 'A::add'); 43 | 44 | echo $server->execute(); 45 | ?> 46 | --EXPECT-- 47 | { 48 | "jsonrpc": "2.0", 49 | "result": 5, 50 | "id": 123456 51 | } -------------------------------------------------------------------------------- /yajl/yajl_alloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /** 18 | * \file yajl_alloc.h 19 | * default memory allocation routines for yajl which use malloc/realloc and 20 | * free 21 | */ 22 | 23 | #ifndef __YAJL_ALLOC_H__ 24 | #define __YAJL_ALLOC_H__ 25 | 26 | #include "api/yajl_common.h" 27 | 28 | #define YA_MALLOC(afs, sz) (afs)->malloc((afs)->ctx, (sz)) 29 | #define YA_FREE(afs, ptr) (afs)->free((afs)->ctx, (ptr)) 30 | #define YA_REALLOC(afs, ptr, sz) (afs)->realloc((afs)->ctx, (ptr), (sz)) 31 | 32 | void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /yajl/yajl_encode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef __YAJL_ENCODE_H__ 18 | #define __YAJL_ENCODE_H__ 19 | 20 | #include "yajl_buf.h" 21 | #include "api/yajl_gen.h" 22 | 23 | void yajl_string_encode(const yajl_print_t printer, 24 | void * ctx, 25 | const unsigned char * str, 26 | size_t length, 27 | int escape_solidus); 28 | 29 | void yajl_string_decode(yajl_buf buf, const unsigned char * str, 30 | size_t length); 31 | 32 | int yajl_string_validate_utf8(const unsigned char * s, size_t len); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /jsr_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef JSR_UTILS_H 22 | #define JSR_UTILS_H 23 | 24 | #include "php.h" 25 | #include "php_ini.h" 26 | #include "ext/standard/info.h" 27 | 28 | static char* jsr_return_zval_type(zend_uchar type); 29 | void jsr_dump_zval(zval *data); 30 | 31 | #endif 32 | 33 | /* 34 | * Local variables: 35 | * tab-width: 4 36 | * c-basic-offset: 4 37 | * End: 38 | * vim600: noet sw=4 ts=4 fdm=marker 39 | * vim<600: noet sw=4 ts=4 40 | */ -------------------------------------------------------------------------------- /yajl/yajl_alloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /** 18 | * \file yajl_alloc.h 19 | * default memory allocation routines for yajl which use malloc/realloc and 20 | * free 21 | */ 22 | 23 | #include "yajl_alloc.h" 24 | #include 25 | 26 | static void * yajl_internal_malloc(void *ctx, size_t sz) 27 | { 28 | (void)ctx; 29 | return malloc(sz); 30 | } 31 | 32 | static void * yajl_internal_realloc(void *ctx, void * previous, 33 | size_t sz) 34 | { 35 | (void)ctx; 36 | return realloc(previous, sz); 37 | } 38 | 39 | static void yajl_internal_free(void *ctx, void * ptr) 40 | { 41 | (void)ctx; 42 | free(ptr); 43 | } 44 | 45 | void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf) 46 | { 47 | yaf->malloc = yajl_internal_malloc; 48 | yaf->free = yajl_internal_free; 49 | yaf->realloc = yajl_internal_realloc; 50 | yaf->ctx = NULL; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /yajl/yajl_buf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef __YAJL_BUF_H__ 18 | #define __YAJL_BUF_H__ 19 | 20 | #include "api/yajl_common.h" 21 | #include "yajl_alloc.h" 22 | 23 | /* 24 | * Implementation/performance notes. If this were moved to a header 25 | * only implementation using #define's where possible we might be 26 | * able to sqeeze a little performance out of the guy by killing function 27 | * call overhead. YMMV. 28 | */ 29 | 30 | /** 31 | * yajl_buf is a buffer with exponential growth. the buffer ensures that 32 | * you are always null padded. 33 | */ 34 | typedef struct yajl_buf_t * yajl_buf; 35 | 36 | /* allocate a new buffer */ 37 | yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc); 38 | 39 | /* free the buffer */ 40 | void yajl_buf_free(yajl_buf buf); 41 | 42 | /* append a number of bytes to the buffer */ 43 | void yajl_buf_append(yajl_buf buf, const void * data, size_t len); 44 | 45 | /* empty the buffer */ 46 | void yajl_buf_clear(yajl_buf buf); 47 | 48 | /* get a pointer to the beginning of the buffer */ 49 | const unsigned char * yajl_buf_data(yajl_buf buf); 50 | 51 | /* get the length of the buffer */ 52 | size_t yajl_buf_len(yajl_buf buf); 53 | 54 | /* truncate the buffer */ 55 | void yajl_buf_truncate(yajl_buf buf, size_t len); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /jsr_epoll.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | #ifndef JSR_EPOLL_H 21 | #define JSR_EPOLL_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define JSR_MAX_EVENTS 32 29 | 30 | typedef struct _jsr_epoll_t jsr_epoll_t; 31 | 32 | struct _jsr_epoll_t 33 | { 34 | int epoll_fd; 35 | struct epoll_event events [JSR_MAX_EVENTS]; 36 | 37 | int loop_total; 38 | }; 39 | 40 | int jsr_setnonblocking(int fd); 41 | 42 | jsr_epoll_t *jsr_epoll_init(); 43 | 44 | int jsr_epoll_add_fd(jsr_epoll_t *self, int fd); 45 | 46 | int jsr_epoll_del_fd(jsr_epoll_t *self, int fd); 47 | 48 | int jsr_epoll_set_in(jsr_epoll_t *self, int fd); 49 | 50 | int jsr_epoll_set_out(jsr_epoll_t *self, int fd); 51 | 52 | int jsr_epoll_loop(jsr_epoll_t *self, int timeout); 53 | 54 | void jsr_epoll_destroy(jsr_epoll_t **self_p); 55 | 56 | #endif 57 | 58 | /* 59 | * Local variables: 60 | * tab-width: 4 61 | * c-basic-offset: 4 62 | * End: 63 | * vim600: noet sw=4 ts=4 fdm=marker 64 | * vim<600: noet sw=4 ts=4 65 | */ -------------------------------------------------------------------------------- /jsr_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef PHP_JSR_SERVER_H 22 | #define PHP_JSR_SERVER_H 23 | 24 | #define COUNT_RECURSIVE 1 25 | 26 | static zend_class_entry *php_jsonrpc_server_entry; 27 | 28 | static int _php_count_recursive(zval *array, long mode TSRMLS_DC); 29 | static zval* _jsr_file_get_contents(); 30 | static zval* _jsr_server_get_arguments(zval *request_params, zval *method_params, 31 | int nb_required_params, int nb_max_params TSRMLS_DC); 32 | 33 | PHP_METHOD(jsonrpc_server, __construct); 34 | PHP_METHOD(jsonrpc_server, register); 35 | PHP_METHOD(jsonrpc_server, bind); 36 | PHP_METHOD(jsonrpc_server, execute); 37 | PHP_METHOD(jsonrpc_server, jsonformat); 38 | PHP_METHOD(jsonrpc_server, rpcformat); 39 | PHP_METHOD(jsonrpc_server, executeprocedure); 40 | PHP_METHOD(jsonrpc_server, executecallback); 41 | PHP_METHOD(jsonrpc_server, executemethod); 42 | PHP_METHOD(jsonrpc_server, getresponse); 43 | 44 | void jsonrpc_server_init(int module_number TSRMLS_DC); 45 | 46 | #endif 47 | 48 | /* 49 | * Local variables: 50 | * tab-width: 4 51 | * c-basic-offset: 4 52 | * End: 53 | * vim600: noet sw=4 ts=4 fdm=marker 54 | * vim<600: noet sw=4 ts=4 55 | */ -------------------------------------------------------------------------------- /examples/jsonrpc-client.php: -------------------------------------------------------------------------------- 1 | call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 5 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 6 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 7 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 8 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 9 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 10 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 11 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 12 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 13 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 14 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 15 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 16 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 17 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 18 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 19 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 20 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 21 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 22 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 23 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 24 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 25 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 26 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 27 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 28 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 29 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 30 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 31 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 32 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 33 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 34 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 35 | $client->call("http://yaf-lib.com/rpc/json", "addition", array(3,5)); 36 | 37 | 38 | $client->execute(); 39 | 40 | 41 | ?> 42 | -------------------------------------------------------------------------------- /yajl/yajl_bytestack.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* 18 | * A header only implementation of a simple stack of bytes, used in YAJL 19 | * to maintain parse state. 20 | */ 21 | 22 | #ifndef __YAJL_BYTESTACK_H__ 23 | #define __YAJL_BYTESTACK_H__ 24 | 25 | #include "api/yajl_common.h" 26 | 27 | #define YAJL_BS_INC 128 28 | 29 | typedef struct yajl_bytestack_t 30 | { 31 | unsigned char * stack; 32 | size_t size; 33 | size_t used; 34 | yajl_alloc_funcs * yaf; 35 | } yajl_bytestack; 36 | 37 | /* initialize a bytestack */ 38 | #define yajl_bs_init(obs, _yaf) { \ 39 | (obs).stack = NULL; \ 40 | (obs).size = 0; \ 41 | (obs).used = 0; \ 42 | (obs).yaf = (_yaf); \ 43 | } \ 44 | 45 | 46 | /* initialize a bytestack */ 47 | #define yajl_bs_free(obs) \ 48 | if ((obs).stack) (obs).yaf->free((obs).yaf->ctx, (obs).stack); 49 | 50 | #define yajl_bs_current(obs) \ 51 | (assert((obs).used > 0), (obs).stack[(obs).used - 1]) 52 | 53 | #define yajl_bs_push(obs, byte) { \ 54 | if (((obs).size - (obs).used) == 0) { \ 55 | (obs).size += YAJL_BS_INC; \ 56 | (obs).stack = (obs).yaf->realloc((obs).yaf->ctx,\ 57 | (void *) (obs).stack, (obs).size);\ 58 | } \ 59 | (obs).stack[((obs).used)++] = (byte); \ 60 | } 61 | 62 | /* removes the top item of the stack, returns nothing */ 63 | #define yajl_bs_pop(obs) { ((obs).used)--; } 64 | 65 | #define yajl_bs_set(obs, byte) \ 66 | (obs).stack[((obs).used) - 1] = (byte); 67 | 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /jsr_list.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef JSR_LIST_H 22 | #define JSR_LIST_H 23 | 24 | #include 25 | #include 26 | 27 | typedef struct _jsr_node_t { 28 | struct _jsr_node_t *next; 29 | void *item; 30 | void (*free_fn)(void *item); 31 | } jsr_node_t; 32 | 33 | typedef struct _jsr_list_t jsr_list_t; 34 | 35 | struct _jsr_list_t { 36 | jsr_node_t *head; 37 | jsr_node_t *tail; 38 | jsr_node_t *cursor; 39 | size_t size; 40 | int autofree; 41 | int (*compare_fn)(void *item1, void *item2); 42 | }; 43 | 44 | jsr_list_t *jsr_list_new(void); 45 | void jsr_list_free(jsr_list_t **self_p); 46 | void jsr_list_destroy(jsr_list_t **self_p); 47 | 48 | void *jsr_list_first(jsr_list_t *self); 49 | void *jsr_list_next(jsr_list_t *self); 50 | void *jsr_list_last(jsr_list_t *self); 51 | 52 | void *jsr_list_head(jsr_list_t *self); 53 | void *jsr_list_tail(jsr_list_t *self); 54 | void *jsr_list_item(jsr_list_t *self); 55 | 56 | int jsr_list_append(jsr_list_t *self, void *item); 57 | int jsr_list_push(jsr_list_t *self, void *item); 58 | void *jsr_list_pop(jsr_list_t *self); 59 | void jsr_list_remove(jsr_list_t *self, void *item); 60 | 61 | int jsr_list_exists(jsr_list_t *self, void *item); 62 | 63 | jsr_list_t *jsr_list_dup(jsr_list_t *self); 64 | 65 | void jsr_list_purge(jsr_list_t *self); 66 | 67 | size_t jsr_list_size(jsr_list_t *self); 68 | 69 | #endif 70 | 71 | /* 72 | * Local variables: 73 | * tab-width: 4 74 | * c-basic-offset: 4 75 | * End: 76 | * vim600: noet sw=4 ts=4 fdm=marker 77 | * vim<600: noet sw=4 ts=4 78 | */ -------------------------------------------------------------------------------- /yajl/yajl_parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef __YAJL_PARSER_H__ 18 | #define __YAJL_PARSER_H__ 19 | 20 | #include "api/yajl_parse.h" 21 | #include "yajl_bytestack.h" 22 | #include "yajl_buf.h" 23 | #include "yajl_lex.h" 24 | 25 | 26 | typedef enum { 27 | yajl_state_start = 0, 28 | yajl_state_parse_complete, 29 | yajl_state_parse_error, 30 | yajl_state_lexical_error, 31 | yajl_state_map_start, 32 | yajl_state_map_sep, 33 | yajl_state_map_need_val, 34 | yajl_state_map_got_val, 35 | yajl_state_map_need_key, 36 | yajl_state_array_start, 37 | yajl_state_array_got_val, 38 | yajl_state_array_need_val, 39 | yajl_state_got_value, 40 | } yajl_state; 41 | 42 | struct yajl_handle_t { 43 | const yajl_callbacks * callbacks; 44 | void * ctx; 45 | yajl_lexer lexer; 46 | const char * parseError; 47 | /* the number of bytes consumed from the last client buffer, 48 | * in the case of an error this will be an error offset, in the 49 | * case of an error this can be used as the error offset */ 50 | size_t bytesConsumed; 51 | /* temporary storage for decoded strings */ 52 | yajl_buf decodeBuf; 53 | /* a stack of states. access with yajl_state_XXX routines */ 54 | yajl_bytestack stateStack; 55 | /* memory allocation routines */ 56 | yajl_alloc_funcs alloc; 57 | /* bitfield */ 58 | unsigned int flags; 59 | }; 60 | 61 | yajl_status 62 | yajl_do_parse(yajl_handle handle, const unsigned char * jsonText, 63 | size_t jsonTextLen); 64 | 65 | yajl_status 66 | yajl_do_finish(yajl_handle handle); 67 | 68 | unsigned char * 69 | yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, 70 | size_t jsonTextLen, int verbose); 71 | 72 | /* A little built in integer parsing routine with the same semantics as strtol 73 | * that's unaffected by LOCALE. */ 74 | long long 75 | yajl_parse_integer(const unsigned char *number, unsigned int length); 76 | 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /yajl/api/yajl_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef __YAJL_COMMON_H__ 18 | #define __YAJL_COMMON_H__ 19 | 20 | #include 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #define YAJL_MAX_DEPTH 128 27 | 28 | /* msft dll export gunk. To build a DLL on windows, you 29 | * must define WIN32, YAJL_SHARED, and YAJL_BUILD. To use a shared 30 | * DLL, you must define YAJL_SHARED and WIN32 */ 31 | #if (defined(_WIN32) || defined(WIN32)) && defined(YAJL_SHARED) 32 | # ifdef YAJL_BUILD 33 | # define YAJL_API __declspec(dllexport) 34 | # else 35 | # define YAJL_API __declspec(dllimport) 36 | # endif 37 | #else 38 | # if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 303 39 | # define YAJL_API __attribute__ ((visibility("default"))) 40 | # else 41 | # define YAJL_API 42 | # endif 43 | #endif 44 | 45 | /** pointer to a malloc function, supporting client overriding memory 46 | * allocation routines */ 47 | typedef void * (*yajl_malloc_func)(void *ctx, size_t sz); 48 | 49 | /** pointer to a free function, supporting client overriding memory 50 | * allocation routines */ 51 | typedef void (*yajl_free_func)(void *ctx, void * ptr); 52 | 53 | /** pointer to a realloc function which can resize an allocation. */ 54 | typedef void * (*yajl_realloc_func)(void *ctx, void * ptr, size_t sz); 55 | 56 | /** A structure which can be passed to yajl_*_alloc routines to allow the 57 | * client to specify memory allocation functions to be used. */ 58 | typedef struct 59 | { 60 | /** pointer to a function that can allocate uninitialized memory */ 61 | yajl_malloc_func malloc; 62 | /** pointer to a function that can resize memory allocations */ 63 | yajl_realloc_func realloc; 64 | /** pointer to a function that can free memory allocated using 65 | * reallocFunction or mallocFunction */ 66 | yajl_free_func free; 67 | /** a context pointer that will be passed to above allocation routines */ 68 | void * ctx; 69 | } yajl_alloc_funcs; 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /jsr_utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #include "jsr_utils.h" 22 | 23 | 24 | static char* jsr_return_zval_type(zend_uchar type) 25 | { 26 | switch (type) { 27 | case IS_NULL: 28 | return "IS_NULL"; 29 | break; 30 | case IS_BOOL: 31 | return "IS_BOOL"; 32 | break; 33 | case IS_LONG: 34 | return "IS_LONG"; 35 | break; 36 | case IS_DOUBLE: 37 | return "IS_DOUBLE"; 38 | break; 39 | case IS_STRING: 40 | return "IS_STRING"; 41 | break; 42 | case IS_ARRAY: 43 | return "IS_ARRAY"; 44 | break; 45 | case IS_OBJECT: 46 | return "IS_OBJECT"; 47 | break; 48 | case IS_RESOURCE: 49 | return "IS_RESOURCE"; 50 | break; 51 | default : 52 | return "unknown"; 53 | } 54 | } 55 | 56 | void jsr_dump_zval(zval *data) 57 | { 58 | php_printf("zval<%p> {\n", data); 59 | php_printf(" refcount__gc -> %d\n", data->refcount__gc); 60 | php_printf(" is_ref__gc -> %d\n", data->is_ref__gc); 61 | php_printf(" type -> %s\n", jsr_return_zval_type(data->type)); 62 | php_printf(" zand_value<%p> {\n", data->value); 63 | 64 | php_printf(" lval -> %d\n", data->value.lval); 65 | php_printf(" dval -> %e\n", data->value.dval); 66 | if (Z_TYPE_P(data) == IS_STRING){ 67 | php_printf(" str -> %s\n", Z_STRVAL_P(data)); 68 | } 69 | php_printf(" *ht<%p> {\n", data->value.ht); 70 | php_printf(" }\n"); 71 | php_printf(" obj<%p> {\n", data->value.obj); 72 | php_printf(" }\n"); 73 | php_printf(" }\n"); 74 | 75 | php_printf("}\n"); 76 | } 77 | 78 | 79 | /* 80 | * Local variables: 81 | * tab-width: 4 82 | * c-basic-offset: 4 83 | * End: 84 | * vim600: noet sw=4 ts=4 fdm=marker 85 | * vim<600: noet sw=4 ts=4 86 | */ -------------------------------------------------------------------------------- /yajl/yajl_buf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "yajl_buf.h" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #define YAJL_BUF_INIT_SIZE 2048 24 | 25 | struct yajl_buf_t { 26 | size_t len; 27 | size_t used; 28 | unsigned char * data; 29 | yajl_alloc_funcs * alloc; 30 | }; 31 | 32 | static 33 | void yajl_buf_ensure_available(yajl_buf buf, size_t want) 34 | { 35 | size_t need; 36 | 37 | assert(buf != NULL); 38 | 39 | /* first call */ 40 | if (buf->data == NULL) { 41 | buf->len = YAJL_BUF_INIT_SIZE; 42 | buf->data = (unsigned char *) YA_MALLOC(buf->alloc, buf->len); 43 | buf->data[0] = 0; 44 | } 45 | 46 | need = buf->len; 47 | 48 | while (want >= (need - buf->used)) need <<= 1; 49 | 50 | if (need != buf->len) { 51 | buf->data = (unsigned char *) YA_REALLOC(buf->alloc, buf->data, need); 52 | buf->len = need; 53 | } 54 | } 55 | 56 | yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc) 57 | { 58 | yajl_buf b = YA_MALLOC(alloc, sizeof(struct yajl_buf_t)); 59 | memset((void *) b, 0, sizeof(struct yajl_buf_t)); 60 | b->alloc = alloc; 61 | return b; 62 | } 63 | 64 | void yajl_buf_free(yajl_buf buf) 65 | { 66 | assert(buf != NULL); 67 | if (buf->data) YA_FREE(buf->alloc, buf->data); 68 | YA_FREE(buf->alloc, buf); 69 | } 70 | 71 | void yajl_buf_append(yajl_buf buf, const void * data, size_t len) 72 | { 73 | yajl_buf_ensure_available(buf, len); 74 | if (len > 0) { 75 | assert(data != NULL); 76 | memcpy(buf->data + buf->used, data, len); 77 | buf->used += len; 78 | buf->data[buf->used] = 0; 79 | } 80 | } 81 | 82 | void yajl_buf_clear(yajl_buf buf) 83 | { 84 | buf->used = 0; 85 | if (buf->data) buf->data[buf->used] = 0; 86 | } 87 | 88 | const unsigned char * yajl_buf_data(yajl_buf buf) 89 | { 90 | return buf->data; 91 | } 92 | 93 | size_t yajl_buf_len(yajl_buf buf) 94 | { 95 | return buf->used; 96 | } 97 | 98 | void 99 | yajl_buf_truncate(yajl_buf buf, size_t len) 100 | { 101 | assert(len <= buf->used); 102 | buf->used = len; 103 | } 104 | -------------------------------------------------------------------------------- /jsr_epoll.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | #include "jsr_epoll.h" 21 | 22 | int jsr_setnonblocking(int fd) 23 | { 24 | int old_option = fcntl(fd, F_GETFL); 25 | int new_option = old_option | O_NONBLOCK; 26 | fcntl(fd, F_SETFL, new_option); 27 | return old_option; 28 | } 29 | 30 | jsr_epoll_t * 31 | jsr_epoll_init() 32 | { 33 | jsr_epoll_t *self = (jsr_epoll_t *)malloc(sizeof(jsr_epoll_t)); 34 | 35 | self->epoll_fd = epoll_create(1024); 36 | 37 | return self; 38 | } 39 | 40 | int 41 | jsr_epoll_add_fd(jsr_epoll_t *self, int fd) 42 | { 43 | struct epoll_event ev; 44 | 45 | ev.events = 0; 46 | ev.data.fd = fd; 47 | 48 | if (epoll_ctl(self->epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) 49 | return -1; 50 | 51 | //jsr_setnonblocking(self->epoll_fd); 52 | 53 | return 0; 54 | } 55 | 56 | int 57 | jsr_epoll_del_fd(jsr_epoll_t *self, int fd) 58 | { 59 | if (epoll_ctl(self->epoll_fd, EPOLL_CTL_DEL, fd, NULL) ==-1) 60 | return -1; 61 | 62 | return 0; 63 | } 64 | 65 | int 66 | jsr_epoll_set_in(jsr_epoll_t *self, int fd) 67 | { 68 | struct epoll_event ev; 69 | 70 | ev.events = 0; 71 | ev.data.fd = fd; 72 | ev.events |= EPOLLIN; 73 | 74 | if (epoll_ctl(self->epoll_fd, EPOLL_CTL_MOD, fd, &ev) == -1) 75 | return -1; 76 | 77 | return 0; 78 | } 79 | 80 | int 81 | jsr_epoll_set_out(jsr_epoll_t *self, int fd) 82 | { 83 | struct epoll_event ev; 84 | 85 | ev.events = 0; 86 | ev.data.fd = fd; 87 | ev.events |= EPOLLOUT; 88 | 89 | if (epoll_ctl(self->epoll_fd, EPOLL_CTL_MOD, fd, &ev) == -1) 90 | return -1; 91 | 92 | return 0; 93 | } 94 | 95 | int 96 | jsr_epoll_loop(jsr_epoll_t *self, int timeout) 97 | { 98 | int res; 99 | 100 | /*while (1){ 101 | res = epoll_wait(self->epoll_fd, self->events, JSR_MAX_EVENTS, timeout); 102 | if (res == -1 && errno == EINTR) 103 | continue; 104 | break; 105 | }*/ 106 | res = epoll_wait(self->epoll_fd, self->events, JSR_MAX_EVENTS, timeout); 107 | 108 | return res; 109 | } 110 | 111 | void 112 | jsr_epoll_destroy(jsr_epoll_t **self_p) 113 | { 114 | if (*self_p) 115 | { 116 | jsr_epoll_t *self = *self_p; 117 | close(self->epoll_fd); 118 | free(self); 119 | *self_p = NULL; 120 | } 121 | } 122 | 123 | 124 | 125 | /* 126 | * Local variables: 127 | * tab-width: 4 128 | * c-basic-offset: 4 129 | * End: 130 | * vim600: noet sw=4 ts=4 fdm=marker 131 | * vim<600: noet sw=4 ts=4 132 | */ -------------------------------------------------------------------------------- /examples/curl-epoll.c: -------------------------------------------------------------------------------- 1 | /** 2 | gcc -o curl-epoll curl-epoll.c ../jsr_curl.c ../jsr_list.c ../jsr_epoll.c -lcurl 3 | */ 4 | 5 | #include "../jsr_curl.h" 6 | #include "../jsr_epoll.h" 7 | 8 | int 9 | socket_callback(CURL *easy, curl_socket_t fd, int action, void *u, void *s) 10 | { 11 | printf(">>> %s: adding fd=%d action=%d\n", __func__, fd, action); 12 | jsr_epoll_t *jsr_epoll = (jsr_epoll_t *) u; 13 | 14 | if (action == CURL_POLL_REMOVE) 15 | { 16 | jsr_epoll_del_fd(jsr_epoll, fd); 17 | } 18 | 19 | if (action == CURL_POLL_IN || action == CURL_POLL_INOUT) 20 | { 21 | printf("in\n"); 22 | jsr_epoll_add_fd(jsr_epoll, fd); 23 | jsr_epoll_set_in(jsr_epoll, fd); 24 | } 25 | 26 | if (action == CURL_POLL_OUT || action == CURL_POLL_INOUT) 27 | { 28 | printf("out\n"); 29 | jsr_epoll_add_fd(jsr_epoll, fd); 30 | jsr_epoll_set_out(jsr_epoll, fd); 31 | } 32 | 33 | return 0; 34 | } 35 | 36 | int 37 | timer_callback(CURLM *multi, long timeout_ms, void *u) 38 | { 39 | printf(">>> %s: timeout: %ld ms\n", __func__, timeout_ms); 40 | return 0; 41 | } 42 | 43 | int 44 | main() 45 | { 46 | jsr_curl_global_new(); 47 | 48 | jsr_epoll_t *jsr_epoll = jsr_epoll_init(); 49 | 50 | jsr_curlm_t *jsr_curlm = jsr_curlm_new(); 51 | 52 | curl_multi_setopt(jsr_curlm->multi_handle, CURLMOPT_SOCKETFUNCTION, socket_callback); 53 | curl_multi_setopt(jsr_curlm->multi_handle, CURLMOPT_SOCKETDATA, jsr_epoll); 54 | curl_multi_setopt(jsr_curlm->multi_handle, CURLMOPT_TIMERFUNCTION, timer_callback); 55 | 56 | jsr_curl_item_t *jsr_curl_item = jsr_curl_item_new( 57 | "http://yaf-lib.com/", 58 | "key=value", 59 | 9 60 | ); 61 | jsr_curl_item_setopt(jsr_curl_item); 62 | jsr_curlm_list_append(jsr_curlm, jsr_curl_item); 63 | 64 | jsr_curl_item_t *jsr_curl_item_2 = jsr_curl_item_new( 65 | "http://yaf-lib.com/", 66 | "key=value", 67 | 9 68 | ); 69 | jsr_curl_item_setopt(jsr_curl_item_2); 70 | jsr_curlm_list_append(jsr_curlm, jsr_curl_item_2); 71 | 72 | jsr_curl_item_t *jsr_curl_item_3 = jsr_curl_item_new( 73 | "http://yaf-lib.com/", 74 | "key=value", 75 | 9 76 | ); 77 | jsr_curl_item_setopt(jsr_curl_item_3); 78 | jsr_curlm_list_append(jsr_curlm, jsr_curl_item_3); 79 | 80 | jsr_curl_item_t *jsr_curl_item_4 = jsr_curl_item_new( 81 | "http://yaf-lib.com/", 82 | "key=value", 83 | 9 84 | ); 85 | jsr_curl_item_setopt(jsr_curl_item_4); 86 | jsr_curlm_list_append(jsr_curlm, jsr_curl_item_4); 87 | 88 | jsr_curl_item_t *jsr_curl_item_5 = jsr_curl_item_new( 89 | "http://yaf-lib.com/", 90 | "key=value", 91 | 9 92 | ); 93 | jsr_curl_item_setopt(jsr_curl_item_5); 94 | jsr_curlm_list_append(jsr_curlm, jsr_curl_item_5); 95 | 96 | jsr_curl_item_t *jsr_curl_item_6 = jsr_curl_item_new( 97 | "http://yaf-lib.com/", 98 | "key=value", 99 | 9 100 | ); 101 | jsr_curl_item_setopt(jsr_curl_item_6); 102 | jsr_curlm_list_append(jsr_curlm, jsr_curl_item_6); 103 | 104 | jsr_curlm_post(jsr_curlm); 105 | 106 | int running_handles = 1; 107 | int loop_total = 0; 108 | while (running_handles > 0) 109 | { 110 | printf(">>> calling epoll_wait\n"); 111 | loop_total = jsr_epoll_loop(jsr_epoll , 1000); 112 | printf("%d\n", loop_total); 113 | 114 | if (loop_total == 0){ 115 | curl_multi_socket_action(jsr_curlm->multi_handle, CURL_SOCKET_TIMEOUT, 0, &running_handles); 116 | } 117 | else 118 | { 119 | int i = 0; 120 | for (i = 0; i < loop_total; i++){ 121 | curl_multi_socket_action(jsr_curlm->multi_handle, jsr_epoll->events[i].data.fd, 0, &running_handles); 122 | } 123 | } 124 | 125 | } 126 | 127 | //curl_multi_setopt(jsr_curl->multi_handle, CURLMOPT_SOCKETFUNCTION, sock_cb); 128 | //curl_multi_setopt(jsr_curl->multi_handle, CURLMOPT_SOCKETDATA, jsr_curl); 129 | 130 | jsr_epoll_destroy(&jsr_epoll); 131 | 132 | jsr_curlm_destroy(&jsr_curlm); 133 | jsr_curl_global_destroy(); 134 | 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension jsonrpc 3 | 4 | dnl Comments in this file start with the string 'dnl'. 5 | dnl Remove where necessary. This file will not work 6 | dnl without editing. 7 | 8 | dnl If your extension references something external, use with: 9 | 10 | dnl PHP_ARG_WITH(jsonrpc, for jsonrpc support, 11 | dnl Make sure that the comment is aligned: 12 | dnl [ --with-jsonrpc Include jsonrpc support]) 13 | 14 | dnl Otherwise use enable: 15 | 16 | PHP_ARG_ENABLE(jsonrpc, whether to enable jsonrpc support, 17 | dnl Make sure that the comment is aligned: 18 | [ --enable-jsonrpc Enable jsonrpc support]) 19 | 20 | PHP_ARG_WITH(curl, for curl protocol support, 21 | [ --with-curl[=DIR] Include curl protocol support]) 22 | 23 | if test "$PHP_JSONRPC" != "no"; then 24 | dnl Write more examples of tests here... 25 | 26 | dnl # --with-jsonrpc -> check with-path 27 | dnl SEARCH_PATH="/usr/local /usr" # you might want to change this 28 | dnl SEARCH_FOR="/include/jsonrpc.h" # you most likely want to change this 29 | dnl if test -r $PHP_JSONRPC/$SEARCH_FOR; then # path given as parameter 30 | dnl JSONRPC_DIR=$PHP_JSONRPC 31 | dnl else # search default path list 32 | dnl AC_MSG_CHECKING([for jsonrpc files in default path]) 33 | dnl for i in $SEARCH_PATH ; do 34 | dnl if test -r $i/$SEARCH_FOR; then 35 | dnl JSONRPC_DIR=$i 36 | dnl AC_MSG_RESULT(found in $i) 37 | dnl fi 38 | dnl done 39 | dnl fi 40 | dnl 41 | dnl if test -z "$JSONRPC_DIR"; then 42 | dnl AC_MSG_RESULT([not found]) 43 | dnl AC_MSG_ERROR([Please reinstall the jsonrpc distribution]) 44 | dnl fi 45 | 46 | dnl # --with-jsonrpc -> add include path 47 | dnl PHP_ADD_INCLUDE($JSONRPC_DIR/include) 48 | 49 | dnl # --with-jsonrpc -> check for lib and symbol presence 50 | dnl LIBNAME=jsonrpc # you may want to change this 51 | dnl LIBSYMBOL=jsonrpc # you most likely want to change this 52 | 53 | dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 54 | dnl [ 55 | dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $JSONRPC_DIR/lib, JSONRPC_SHARED_LIBADD) 56 | dnl AC_DEFINE(HAVE_JSONRPCLIB,1,[ ]) 57 | dnl ],[ 58 | dnl AC_MSG_ERROR([wrong jsonrpc lib version or lib not found]) 59 | dnl ],[ 60 | dnl -L$JSONRPC_DIR/lib -lm 61 | dnl ]) 62 | dnl 63 | dnl PHP_SUBST(JSONRPC_SHARED_LIBADD) 64 | 65 | YAJL_SOURCES="\ 66 | yajl/yajl_version.c \ 67 | yajl/yajl.c \ 68 | yajl/yajl_encode.c \ 69 | yajl/yajl_lex.c \ 70 | yajl/yajl_parser.c \ 71 | yajl/yajl_buf.c \ 72 | yajl/yajl_tree.c \ 73 | yajl/yajl_alloc.c \ 74 | yajl/yajl_gen.c" 75 | 76 | for i in /usr /usr/local /opt; do 77 | if test -f $i/include/curl/easy.h; then 78 | JSONRPC_CURL_DIR=$i 79 | fi 80 | done 81 | 82 | if test -z "$JSONRPC_CURL_DIR"; then 83 | AC_MSG_RESULT(not found) 84 | AC_MSG_ERROR(Please reinstall the libcurl distribution - 85 | easy.h should be in /include/curl/) 86 | fi 87 | 88 | AC_MSG_CHECKING([test test jsonrpc $JSONRPC_CURL_DIR]) 89 | 90 | PHP_ADD_INCLUDE($JSONRPC_CURL_DIR/include) 91 | dnl PHP_ADD_LIBRARY_WITH_PATH(curl, $JSONRPC_CURL_DIR/lib, JSONRPC_CURL_SHARED_LIBADD) 92 | 93 | LIBNAME=curl # you may want to change this 94 | LIBSYMBOL=curl_version # you most likely want to change this 95 | 96 | PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 97 | [ 98 | PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $JSONRPC_CURL_DIR/lib, JSONRPC_SHARED_LIBADD) 99 | AC_DEFINE(HAVE_JSONRPC_CURL_LIB,1,[ ]) 100 | ],[ 101 | AC_MSG_ERROR([wrong curl lib version or lib not found]) 102 | ],[ 103 | -L$JSONRPC_CURL_DIR/lib -lm 104 | ]) 105 | 106 | PHP_SUBST(JSONRPC_SHARED_LIBADD) 107 | 108 | 109 | PHP_NEW_EXTENSION(jsonrpc, jsonrpc.c jsr_server.c jsr_client.c jsr_curl.c jsr_list.c jsr_epoll.c jsr_utils.c $YAJL_SOURCES jsr_yajl.c, $ext_shared) 110 | fi 111 | -------------------------------------------------------------------------------- /php_jsonrpc.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef PHP_JSONRPC_H 22 | #define PHP_JSONRPC_H 23 | 24 | #include "php.h" 25 | //#include "ext/standard/php_smart_str.h" 26 | 27 | #define PHP_JSONRPC_VERSION "0.0.9" 28 | 29 | #define PHP_JSONRPC_DEBUG 0 30 | 31 | extern zend_module_entry jsonrpc_module_entry; 32 | #define phpext_jsonrpc_ptr &jsonrpc_module_entry 33 | 34 | #ifdef PHP_WIN32 35 | # define PHP_JSONRPC_API __declspec(dllexport) 36 | #elif defined(__GNUC__) && __GNUC__ >= 4 37 | # define PHP_JSONRPC_API __attribute__ ((visibility("default"))) 38 | #else 39 | # define PHP_JSONRPC_API 40 | #endif 41 | 42 | #ifdef ZTS 43 | #include "TSRM.h" 44 | #endif 45 | 46 | #if PHP_VERSION_ID < 70000 47 | #define JSR_ZVAL_STRINGL(zval, name, len, dup){ \ 48 | ZVAL_STRINGL(zval, name, len, dup);\ 49 | } 50 | #else 51 | #define JSR_ZVAL_STRINGL(zval, name, len, dup){ \ 52 | ZVAL_STRINGL(zval, name, len);\ 53 | } 54 | #endif 55 | 56 | #if PHP_VERSION_ID < 70000 57 | #define JSR_ZVAL_STRING(zval, name, dup){ \ 58 | ZVAL_STRING(zval, name, dup);\ 59 | } 60 | #else 61 | #define JSR_ZVAL_STRING(zval, name, dup){ \ 62 | ZVAL_STRINGL(zval, name);\ 63 | } 64 | #endif 65 | 66 | #if PHP_VERSION_ID < 70000 67 | #define JSR_STRING char 68 | #else 69 | #define JSR_STRING zend_string 70 | #endif 71 | 72 | extern int le_jsr_epoll_persist; 73 | extern int le_jsr_curlm_persist; 74 | 75 | PHP_MINIT_FUNCTION(jsonrpc); 76 | PHP_MSHUTDOWN_FUNCTION(jsonrpc); 77 | PHP_RINIT_FUNCTION(jsonrpc); 78 | PHP_RSHUTDOWN_FUNCTION(jsonrpc); 79 | PHP_MINFO_FUNCTION(jsonrpc); 80 | 81 | PHP_FUNCTION(confirm_jsonrpc_compiled); /* For testing, remove later. */ 82 | 83 | /* 84 | Declare any global variables you may need between the BEGIN 85 | and END macros here: 86 | 87 | ZEND_BEGIN_MODULE_GLOBALS(jsonrpc) 88 | long global_value; 89 | char *global_string; 90 | ZEND_END_MODULE_GLOBALS(jsonrpc) 91 | */ 92 | 93 | /* In every utility function you add that needs to use variables 94 | in php_jsonrpc_globals, call TSRMLS_FETCH(); after declaring other 95 | variables used by that function, or better yet, pass in TSRMLS_CC 96 | after the last function argument and declare your utility function 97 | with TSRMLS_DC after the last declared argument. Always refer to 98 | the globals in your function as JSONRPC_G(variable). You are 99 | encouraged to rename these macros something shorter, see 100 | examples in any other php module directory. 101 | */ 102 | 103 | #ifdef ZTS 104 | #define JSONRPC_G(v) TSRMG(jsonrpc_globals_id, zend_jsonrpc_globals *, v) 105 | #else 106 | #define JSONRPC_G(v) (jsonrpc_globals.v) 107 | #endif 108 | 109 | #endif /* PHP_JSONRPC_H */ 110 | 111 | 112 | /* 113 | * Local variables: 114 | * tab-width: 4 115 | * c-basic-offset: 4 116 | * End: 117 | * vim600: noet sw=4 ts=4 fdm=marker 118 | * vim<600: noet sw=4 ts=4 119 | */ 120 | -------------------------------------------------------------------------------- /jsr_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef PHP_JSR_CLIENT_H 22 | #define PHP_JSR_CLIENT_H 23 | 24 | #include "jsr_epoll.h" 25 | #include "jsr_curl.h" 26 | 27 | #define COUNT_RECURSIVE 1 28 | 29 | typedef struct _php_jsr_epoll_context { 30 | 31 | jsr_epoll_t *epoll; 32 | 33 | zend_bool is_persistent; 34 | 35 | //zend_bool is_global; 36 | 37 | } php_jsr_epoll_context; 38 | 39 | 40 | typedef struct _php_jsr_curlm_conn { 41 | 42 | jsr_curlm_t *curlm; 43 | 44 | zend_bool is_persistent; 45 | 46 | } php_jsr_curlm_conn; 47 | 48 | 49 | typedef struct _php_jsr_request_object { 50 | zend_object zo; 51 | 52 | //jsr_epoll_t *epoll; 53 | php_jsr_epoll_context *context; 54 | 55 | jsr_curlm_t *curlm; 56 | 57 | zend_bool executed; 58 | 59 | zend_bool is_persistent; 60 | 61 | } php_jsr_reuqest_object; 62 | 63 | #endif 64 | 65 | 66 | static zend_class_entry *php_jsonrpc_client_entry; 67 | static zend_class_entry *php_jsonrpc_client_request_entry; 68 | 69 | static zend_object_handlers jsr_request_object_handlers; 70 | 71 | static int _php_count_recursive(zval *array, long mode TSRMLS_DC); 72 | 73 | static zval* _jsr_client_prepare_request(zval *procedure, zval *params, zval *custom_id TSRMLS_DC); 74 | 75 | static zval* _php_jsr_response_error(long code, char *message, jsr_payload_id *payload_id); 76 | 77 | static int _socket_callback(CURL *easy, curl_socket_t fd, int action, void *u, void *s); 78 | static int _timer_callback(CURLM *multi, long timeout_ms, void *u); 79 | 80 | static zend_object_value _php_jsr_request_object_new(zend_class_entry *class_type TSRMLS_DC); 81 | static void _php_jsr_request_object_free_storage(void *object TSRMLS_DC); 82 | 83 | static size_t _write_callback(char *ptr, size_t size, size_t nmemb, void *ctx); 84 | 85 | static php_jsr_epoll_context *_php_jsr_epoll_new(zend_bool is_persistent TSRMLS_DC); 86 | static php_jsr_epoll_context *_php_jsr_epoll_get(zend_bool is_persistent TSRMLS_DC); 87 | static void _php_jsr_epoll_destroy(php_jsr_epoll_context *context); 88 | ZEND_RSRC_DTOR_FUNC(_php_jsr_epoll_dtor); 89 | 90 | static php_jsr_curlm_conn *_php_jsr_curlm_new(zend_bool is_persistent TSRMLS_DC); 91 | static php_jsr_curlm_conn *_php_jsr_curlm_get(zend_bool is_persistent TSRMLS_DC); 92 | static void _php_jsr_curlm_destroy(php_jsr_curlm_conn *conn); 93 | ZEND_RSRC_DTOR_FUNC(_php_jsr_curlm_dtor); 94 | 95 | PHP_METHOD(jsonrpc_client, __construct); 96 | PHP_METHOD(jsonrpc_client, __destruct); 97 | PHP_METHOD(jsonrpc_client, call); 98 | PHP_METHOD(jsonrpc_client, execute); 99 | PHP_METHOD(jsonrpc_client, connect); 100 | PHP_METHOD(jsonrpc_client, __call); 101 | PHP_METHOD(jsonrpc_client, authentication); 102 | 103 | void jsonrpc_client_init(int module_number TSRMLS_DC); 104 | /* 105 | * Local variables: 106 | * tab-width: 4 107 | * c-basic-offset: 4 108 | * End: 109 | * vim600: noet sw=4 ts=4 fdm=marker 110 | * vim<600: noet sw=4 ts=4 111 | */ -------------------------------------------------------------------------------- /jsr_yajl.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | #ifndef JSR_YAJL_H 21 | #define JSR_YAJL_H 22 | 23 | #include "php.h" 24 | #include "php_ini.h" 25 | #include "ext/standard/info.h" 26 | 27 | #include "yajl/api/yajl_version.h" 28 | #include "yajl/api/yajl_gen.h" 29 | #include "yajl/api/yajl_parse.h" 30 | #include "yajl/yajl_parser.h" 31 | 32 | #define PHP_YAJL_VERSION "0.0.1" 33 | 34 | #define PHP_JSON_OUTPUT_ARRAY 0 35 | #define PHP_JSON_OUTPUT_OBJECT 1 36 | 37 | #define STATUS_CONTINUE 1 38 | #define STATUS_ABORT 0 39 | 40 | #define RETURN_ERROR(ctx,retval,...) { \ 41 | if ((ctx)->errbuf != NULL) \ 42 | snprintf ((ctx)->errbuf, (ctx)->errbuf_size, __VA_ARGS__); \ 43 | return (retval); \ 44 | } 45 | 46 | struct stack_elem_s; 47 | typedef struct stack_elem_s stack_elem_t; 48 | struct stack_elem_s 49 | { 50 | zval *key; 51 | zval *value; 52 | stack_elem_t *next; 53 | int type; // 0: map; 1: array 54 | }; 55 | 56 | struct context_s 57 | { 58 | stack_elem_t *stack; 59 | zval *root; 60 | char *errbuf; 61 | size_t errbuf_size; 62 | }; 63 | typedef struct context_s context_t; 64 | 65 | typedef struct _php_yajl_t { 66 | 67 | yajl_gen gen; 68 | 69 | yajl_handle handle; 70 | yajl_status status; 71 | context_t *ctx; 72 | 73 | } php_yajl_t; 74 | 75 | static zend_class_entry *php_jsonrpc_yajl_class_entry; 76 | 77 | static int json_determine_array_type(zval **val TSRMLS_DC); 78 | static void php_yajl_generate_array(yajl_gen gen, zval *val TSRMLS_DC); 79 | void php_yajl_generate(yajl_gen gen, zval *val TSRMLS_DC); 80 | 81 | static int object_add_keyval(context_t *ctx, zval *obj, zval *key, zval *value); 82 | static int array_add_value (context_t *ctx, zval *array, zval *value); 83 | static int context_push(context_t *ctx, zval *v, int type); 84 | static zval* context_pop(context_t *ctx); 85 | static int context_add_value (context_t *ctx, zval *v); 86 | static int handle_string (void *ctx, const unsigned char *string, size_t string_length); 87 | static int handle_number (void *ctx, const char *string, size_t string_length); 88 | static int handle_integer(void *ctx, long long int value); 89 | static int handle_double(void *ctx, double value); 90 | static int handle_start_map (void *ctx); 91 | static int handle_end_map (void *ctx); 92 | static int handle_start_array (void *ctx); 93 | static int handle_end_array (void *ctx); 94 | static int handle_boolean (void *ctx, int boolean_value); 95 | static int handle_null (void *ctx); 96 | zval* yajl_zval_parse (const char *input, char *error_buffer, size_t error_buffer_size); 97 | void yajl_zval_free (zval *v); 98 | 99 | static void php_yajl_rsrc_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC); 100 | static zval* php_yajl_new(); 101 | 102 | PHP_METHOD(jsonrpc_yajl, generate); 103 | PHP_METHOD(jsonrpc_yajl, parse); 104 | 105 | void jsonrpc_yajl_init(int module_number TSRMLS_DC); 106 | 107 | #endif 108 | /* 109 | * Local variables: 110 | * tab-width: 4 111 | * c-basic-offset: 4 112 | * End: 113 | * vim600: noet sw=4 ts=4 fdm=marker 114 | * vim<600: noet sw=4 ts=4 115 | */ -------------------------------------------------------------------------------- /yajl/yajl_lex.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef __YAJL_LEX_H__ 18 | #define __YAJL_LEX_H__ 19 | 20 | #include "api/yajl_common.h" 21 | 22 | typedef enum { 23 | yajl_tok_bool, 24 | yajl_tok_colon, 25 | yajl_tok_comma, 26 | yajl_tok_eof, 27 | yajl_tok_error, 28 | yajl_tok_left_brace, 29 | yajl_tok_left_bracket, 30 | yajl_tok_null, 31 | yajl_tok_right_brace, 32 | yajl_tok_right_bracket, 33 | 34 | /* we differentiate between integers and doubles to allow the 35 | * parser to interpret the number without re-scanning */ 36 | yajl_tok_integer, 37 | yajl_tok_double, 38 | 39 | /* we differentiate between strings which require further processing, 40 | * and strings that do not */ 41 | yajl_tok_string, 42 | yajl_tok_string_with_escapes, 43 | 44 | /* comment tokens are not currently returned to the parser, ever */ 45 | yajl_tok_comment 46 | } yajl_tok; 47 | 48 | typedef struct yajl_lexer_t * yajl_lexer; 49 | 50 | yajl_lexer yajl_lex_alloc(yajl_alloc_funcs * alloc, 51 | unsigned int allowComments, 52 | unsigned int validateUTF8); 53 | 54 | void yajl_lex_free(yajl_lexer lexer); 55 | 56 | /** 57 | * run/continue a lex. "offset" is an input/output parameter. 58 | * It should be initialized to zero for a 59 | * new chunk of target text, and upon subsetquent calls with the same 60 | * target text should passed with the value of the previous invocation. 61 | * 62 | * the client may be interested in the value of offset when an error is 63 | * returned from the lexer. This allows the client to render useful 64 | * error messages. 65 | * 66 | * When you pass the next chunk of data, context should be reinitialized 67 | * to zero. 68 | * 69 | * Finally, the output buffer is usually just a pointer into the jsonText, 70 | * however in cases where the entity being lexed spans multiple chunks, 71 | * the lexer will buffer the entity and the data returned will be 72 | * a pointer into that buffer. 73 | * 74 | * This behavior is abstracted from client code except for the performance 75 | * implications which require that the client choose a reasonable chunk 76 | * size to get adequate performance. 77 | */ 78 | yajl_tok yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, 79 | size_t jsonTextLen, size_t * offset, 80 | const unsigned char ** outBuf, size_t * outLen); 81 | 82 | /** have a peek at the next token, but don't move the lexer forward */ 83 | yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, 84 | size_t jsonTextLen, size_t offset); 85 | 86 | 87 | typedef enum { 88 | yajl_lex_e_ok = 0, 89 | yajl_lex_string_invalid_utf8, 90 | yajl_lex_string_invalid_escaped_char, 91 | yajl_lex_string_invalid_json_char, 92 | yajl_lex_string_invalid_hex_char, 93 | yajl_lex_invalid_char, 94 | yajl_lex_invalid_string, 95 | yajl_lex_missing_integer_after_decimal, 96 | yajl_lex_missing_integer_after_exponent, 97 | yajl_lex_missing_integer_after_minus, 98 | yajl_lex_unallowed_comment 99 | } yajl_lex_error; 100 | 101 | const char * yajl_lex_error_to_string(yajl_lex_error error); 102 | 103 | /** allows access to more specific information about the lexical 104 | * error when yajl_lex_lex returns yajl_tok_error. */ 105 | yajl_lex_error yajl_lex_get_error(yajl_lexer lexer); 106 | 107 | /** get the current offset into the most recently lexed json string. */ 108 | size_t yajl_lex_current_offset(yajl_lexer lexer); 109 | 110 | /** get the number of lines lexed by this lexer instance */ 111 | size_t yajl_lex_current_line(yajl_lexer lexer); 112 | 113 | /** get the number of chars lexed by this lexer instance since the last 114 | * \n or \r */ 115 | size_t yajl_lex_current_char(yajl_lexer lexer); 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /jsr_curl.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef JSR_CURL_H 22 | #define JSR_CURL_H 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | #include "php.h" 36 | #include "php_ini.h" 37 | #include "ext/standard/info.h" 38 | 39 | #include "jsr_list.h" 40 | #include "jsr_utils.h" 41 | 42 | typedef struct _jsr_curl_t jsr_curl_t; 43 | typedef struct _jsr_curlm_t jsr_curlm_t; 44 | typedef struct _jsr_payload_id jsr_payload_id; 45 | typedef struct _jsr_curl_item_t jsr_curl_item_t; 46 | typedef struct _jsr_curl_sockinfo_t jsr_curl_sockinfo_t; 47 | 48 | struct _jsr_curl_t { 49 | CURL *curl_handle; 50 | CURLM *multi_handle; 51 | CURLcode res; 52 | 53 | FILE *fp; 54 | struct curl_slist *headers; 55 | }; 56 | 57 | struct _jsr_curlm_t 58 | { 59 | CURLM *multi_handle; 60 | jsr_list_t *list; 61 | 62 | int running_handles; 63 | 64 | int timeout; 65 | 66 | struct schedule *s; 67 | }; 68 | 69 | struct _jsr_payload_id { 70 | int type; 71 | 72 | long long_id; 73 | char char_id[64]; 74 | }; 75 | 76 | struct _jsr_curl_item_t { 77 | CURL *curl_handle; 78 | char url[256]; 79 | int timeout; 80 | int verbose; 81 | 82 | struct curl_slist *slist; 83 | char post_field[512]; 84 | size_t post_field_size; 85 | //FILE *fp; 86 | 87 | size_t (*write_callback)(char *ptr, size_t size, size_t nmemb, void *ctx); 88 | size_t (*read_callback)(void *ptr, size_t size, size_t nmemb, void *ctx); 89 | char *write_data; 90 | size_t write_length; 91 | 92 | zval *object; 93 | int response_id; 94 | 95 | jsr_payload_id payload_id; 96 | 97 | }; 98 | 99 | struct _jsr_curl_sockinfo_t { 100 | curl_socket_t sockfd; 101 | CURL *easy; 102 | int action; 103 | long timeout; 104 | }; 105 | 106 | typedef char bool; 107 | #define TRUE 1 108 | 109 | #define jsr_curl_setopt curl_easy_set_opt; 110 | 111 | #define JSR_CURL_GLOBAL_ALL CURL_GLOBAL_ALL; 112 | #define JSR_CURL_GLOBAL_SSL CURL_GLOBAL_SSL; 113 | #define JSR_CURL_GLOBAL_WIN32 CURL_GLOBAL_WIN32; 114 | #define JSR_CURL_GLOBAL_NOTHING CURL_GLOBAL_NOTHING; 115 | #define JSR_CURL_GLOBAL_DEFAULT CURL_GLOBAL_DEFAULT 116 | 117 | void *jsr_curl_global_new(void); 118 | void *jsr_curl_global_destroy(void); 119 | 120 | jsr_curl_t *jsr_curl_new(void); 121 | void *jsr_curl_destroy(jsr_curl_t **self_p); 122 | 123 | jsr_curlm_t *jsr_curlm_new(void); 124 | void *jsr_curlm_destroy(jsr_curlm_t **self_p); 125 | int jsr_curlm_list_append(jsr_curlm_t *self, jsr_curl_item_t *item); 126 | int jsr_curlm_list_remove(jsr_curlm_t *self, jsr_curl_item_t *item); 127 | void *jsr_curlm_add_post(jsr_curlm_t *self); 128 | 129 | jsr_curl_item_t *jsr_curl_item_new(char *url, size_t url_size, char *field, size_t field_size, int response_id, zval *payload_id); 130 | void *jsr_curl_item_destroy(jsr_curl_item_t **self_p); 131 | void *jsr_curl_item_setopt(jsr_curl_item_t *self); 132 | 133 | static void _jsr_curl_dump(const char *text, FILE *stream, unsigned char *ptr, size_t size, bool nohex); 134 | static int _jsr_curl_trace(CURL *handle, curl_infotype type, unsigned char *data, size_t size, void *userp); 135 | 136 | #endif 137 | 138 | /* 139 | * Local variables: 140 | * tab-width: 4 141 | * c-basic-offset: 4 142 | * End: 143 | * vim600: noet sw=4 ts=4 fdm=marker 144 | * vim<600: noet sw=4 ts=4 145 | */ -------------------------------------------------------------------------------- /yajl/yajl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "api/yajl_parse.h" 18 | #include "yajl_lex.h" 19 | #include "yajl_parser.h" 20 | #include "yajl_alloc.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | const char * 28 | yajl_status_to_string(yajl_status stat) 29 | { 30 | const char * statStr = "unknown"; 31 | switch (stat) { 32 | case yajl_status_ok: 33 | statStr = "ok, no error"; 34 | break; 35 | case yajl_status_client_canceled: 36 | statStr = "client canceled parse"; 37 | break; 38 | case yajl_status_error: 39 | statStr = "parse error"; 40 | break; 41 | } 42 | return statStr; 43 | } 44 | 45 | yajl_handle 46 | yajl_alloc(const yajl_callbacks * callbacks, 47 | yajl_alloc_funcs * afs, 48 | void * ctx) 49 | { 50 | yajl_handle hand = NULL; 51 | yajl_alloc_funcs afsBuffer; 52 | 53 | /* first order of business is to set up memory allocation routines */ 54 | if (afs != NULL) { 55 | if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) 56 | { 57 | return NULL; 58 | } 59 | } else { 60 | yajl_set_default_alloc_funcs(&afsBuffer); 61 | afs = &afsBuffer; 62 | } 63 | 64 | hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t)); 65 | 66 | /* copy in pointers to allocation routines */ 67 | memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); 68 | 69 | hand->callbacks = callbacks; 70 | hand->ctx = ctx; 71 | hand->lexer = NULL; 72 | hand->bytesConsumed = 0; 73 | hand->decodeBuf = yajl_buf_alloc(&(hand->alloc)); 74 | hand->flags = 0; 75 | yajl_bs_init(hand->stateStack, &(hand->alloc)); 76 | yajl_bs_push(hand->stateStack, yajl_state_start); 77 | 78 | return hand; 79 | } 80 | 81 | int 82 | yajl_config(yajl_handle h, yajl_option opt, ...) 83 | { 84 | int rv = 1; 85 | va_list ap; 86 | va_start(ap, opt); 87 | 88 | switch(opt) { 89 | case yajl_allow_comments: 90 | case yajl_dont_validate_strings: 91 | case yajl_allow_trailing_garbage: 92 | case yajl_allow_multiple_values: 93 | case yajl_allow_partial_values: 94 | if (va_arg(ap, int)) h->flags |= opt; 95 | else h->flags &= ~opt; 96 | break; 97 | default: 98 | rv = 0; 99 | } 100 | va_end(ap); 101 | 102 | return rv; 103 | } 104 | 105 | void 106 | yajl_free(yajl_handle handle) 107 | { 108 | yajl_bs_free(handle->stateStack); 109 | yajl_buf_free(handle->decodeBuf); 110 | if (handle->lexer) { 111 | yajl_lex_free(handle->lexer); 112 | handle->lexer = NULL; 113 | } 114 | YA_FREE(&(handle->alloc), handle); 115 | } 116 | 117 | yajl_status 118 | yajl_parse(yajl_handle hand, const unsigned char * jsonText, 119 | size_t jsonTextLen) 120 | { 121 | yajl_status status; 122 | 123 | /* lazy allocation of the lexer */ 124 | if (hand->lexer == NULL) { 125 | hand->lexer = yajl_lex_alloc(&(hand->alloc), 126 | hand->flags & yajl_allow_comments, 127 | !(hand->flags & yajl_dont_validate_strings)); 128 | } 129 | 130 | status = yajl_do_parse(hand, jsonText, jsonTextLen); 131 | return status; 132 | } 133 | 134 | 135 | yajl_status 136 | yajl_complete_parse(yajl_handle hand) 137 | { 138 | /* The lexer is lazy allocated in the first call to parse. if parse is 139 | * never called, then no data was provided to parse at all. This is a 140 | * "premature EOF" error unless yajl_allow_partial_values is specified. 141 | * allocating the lexer now is the simplest possible way to handle this 142 | * case while preserving all the other semantics of the parser 143 | * (multiple values, partial values, etc). */ 144 | if (hand->lexer == NULL) { 145 | hand->lexer = yajl_lex_alloc(&(hand->alloc), 146 | hand->flags & yajl_allow_comments, 147 | !(hand->flags & yajl_dont_validate_strings)); 148 | } 149 | 150 | return yajl_do_finish(hand); 151 | } 152 | 153 | unsigned char * 154 | yajl_get_error(yajl_handle hand, int verbose, 155 | const unsigned char * jsonText, size_t jsonTextLen) 156 | { 157 | return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose); 158 | } 159 | 160 | size_t 161 | yajl_get_bytes_consumed(yajl_handle hand) 162 | { 163 | if (!hand) return 0; 164 | else return hand->bytesConsumed; 165 | } 166 | 167 | 168 | void 169 | yajl_free_error(yajl_handle hand, unsigned char * str) 170 | { 171 | /* use memory allocation functions if set */ 172 | YA_FREE(&(hand->alloc), str); 173 | } 174 | 175 | /* XXX: add utility routines to parse from file */ 176 | -------------------------------------------------------------------------------- /jsr_list.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #include "jsr_list.h" 22 | 23 | jsr_list_t * 24 | jsr_list_new(void) 25 | { 26 | jsr_list_t *self = (jsr_list_t *)malloc(sizeof(jsr_list_t)); 27 | self->tail = NULL; 28 | self->size = 0; 29 | return self; 30 | } 31 | 32 | void 33 | jsr_list_free(jsr_list_t **self_p) 34 | { 35 | if (*self_p){ 36 | jsr_list_t *self = *self_p; 37 | free(self); 38 | *self_p = NULL; 39 | } 40 | } 41 | 42 | void 43 | jsr_list_destroy(jsr_list_t **self_p) 44 | { 45 | if (*self_p){ 46 | jsr_list_t *self = *self_p; 47 | jsr_list_purge(self); 48 | free(self); 49 | *self_p = NULL; 50 | } 51 | } 52 | 53 | void * 54 | jsr_list_first(jsr_list_t *self) 55 | { 56 | self->cursor = self->head; 57 | if (self->cursor) 58 | return self->cursor->item; 59 | else 60 | return NULL; 61 | } 62 | 63 | void * 64 | jsr_list_next(jsr_list_t *self) 65 | { 66 | if (self->cursor) 67 | self->cursor = self->cursor->next; 68 | else 69 | self->cursor = self->head; 70 | 71 | if (self->cursor) 72 | return self->cursor->item; 73 | else 74 | return NULL; 75 | } 76 | 77 | void * 78 | jsr_list_last(jsr_list_t *self) 79 | { 80 | self->cursor = self->tail; 81 | if (self->cursor) 82 | return self->cursor->item; 83 | else 84 | return NULL; 85 | } 86 | 87 | void * 88 | jsr_list_head(jsr_list_t *self) 89 | { 90 | return self->head ? self->head->item : NULL; 91 | } 92 | 93 | void * 94 | jsr_list_tail(jsr_list_t *self) 95 | { 96 | return self->tail ? self->tail->item : NULL; 97 | } 98 | 99 | void * 100 | jsr_list_item(jsr_list_t *self) 101 | { 102 | if (self->cursor) 103 | return self->cursor->item; 104 | else 105 | return NULL; 106 | } 107 | 108 | int 109 | jsr_list_append(jsr_list_t *self, void *item) 110 | { 111 | if (!item) 112 | return -1; 113 | 114 | jsr_node_t *node; 115 | node = (jsr_node_t *)malloc(sizeof(jsr_node_t)); 116 | 117 | if (!node) 118 | return -1; 119 | 120 | //if (self->autofree) 121 | // item = strdup((char *)item); 122 | 123 | node->item = item; 124 | if (self->tail) 125 | self->tail->next = node; 126 | else 127 | self->head = node; 128 | 129 | self->tail = node; 130 | node->next = NULL; 131 | 132 | self->size++; 133 | self->cursor = NULL; 134 | 135 | return 0; 136 | } 137 | 138 | int 139 | jsr_list_push(jsr_list_t *self, void *item) 140 | { 141 | jsr_node_t *node; 142 | node = (jsr_node_t *)malloc(sizeof(jsr_node_t)); 143 | 144 | if (!node) 145 | return -1; 146 | 147 | //if (self->autofree) 148 | // item = strdup((char *) item); 149 | 150 | node->item = item; 151 | node->next = self->head; 152 | self->head = node; 153 | 154 | if (self->tail == NULL) 155 | self->tail = node; 156 | 157 | self->size++; 158 | self->cursor = NULL; 159 | 160 | return 0; 161 | } 162 | 163 | void * 164 | jsr_list_pop(jsr_list_t *self) 165 | { 166 | jsr_node_t *node = self->head; 167 | void *item = NULL; 168 | if (node){ 169 | item = node->item; 170 | self->head = node->next; 171 | if (self->tail == node) 172 | self->tail = NULL; 173 | free(node); 174 | self->size--; 175 | } 176 | self->cursor = NULL; 177 | return item; 178 | } 179 | 180 | void jsr_list_remove(jsr_list_t *self, void *item) 181 | { 182 | jsr_node_t *node, *prev = NULL; 183 | 184 | // First off, we need to find the list node 185 | for (node = self->head; node != NULL; node = node->next) { 186 | if (self->compare_fn) { 187 | if ((*self->compare_fn)(node->item, item) == 0) 188 | break; 189 | } 190 | else { 191 | if (node->item == item) 192 | break; 193 | } 194 | prev = node; 195 | } 196 | if (node) { 197 | if (prev) 198 | prev->next = node->next; 199 | else 200 | self->head = node->next; 201 | 202 | if (node->next == NULL) 203 | self->tail = prev; 204 | if (self->cursor == node) 205 | self->cursor = prev; 206 | 207 | //if (node->free_fn) 208 | // (node->free_fn)(node->item); 209 | 210 | free (node); 211 | self->size--; 212 | } 213 | } 214 | 215 | int 216 | jsr_list_exists(jsr_list_t *self, void *item) 217 | { 218 | jsr_node_t *node = self->head; 219 | 220 | while (node){ 221 | if (self->compare_fn){ 222 | if ((*self->compare_fn)(node->item, item) == 0) 223 | return 1; 224 | } 225 | else{ 226 | if (node->item = item){ 227 | return 1; 228 | } 229 | } 230 | node = node->next; 231 | } 232 | 233 | return 0; 234 | } 235 | 236 | //jsr_list_t *jsr_list_dup(jsr_list_t *self); 237 | 238 | void 239 | jsr_list_purge(jsr_list_t *self) 240 | { 241 | jsr_node_t *node = self->head; 242 | while (node){ 243 | jsr_node_t *next = node->next; 244 | if (self->autofree){ 245 | free(node->item); 246 | } 247 | else { 248 | if (node->free_fn) 249 | (node->free_fn)(node->item); 250 | } 251 | 252 | free(node); 253 | node = next; 254 | } 255 | self->head = NULL; 256 | self->tail = NULL; 257 | self->cursor = NULL; 258 | self->size = 0; 259 | } 260 | 261 | size_t 262 | jsr_list_size(jsr_list_t *self) 263 | { 264 | return self->size; 265 | } 266 | 267 | /* 268 | * Local variables: 269 | * tab-width: 4 270 | * c-basic-offset: 4 271 | * End: 272 | * vim600: noet sw=4 ts=4 fdm=marker 273 | * vim<600: noet sw=4 ts=4 274 | */ -------------------------------------------------------------------------------- /jsonrpc.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include "php.h" 26 | #include "php_ini.h" 27 | #include "ext/standard/info.h" 28 | #include "ext/standard/basic_functions.h" 29 | #include "ext/standard/php_rand.h" 30 | #include "SAPI.h" 31 | #include "ext/json/php_json.h" 32 | #include "ext/standard/file.h" 33 | #include "php_jsonrpc.h" 34 | 35 | #include "jsr_yajl.h" 36 | #include "jsr_client.h" 37 | #include "jsr_server.h" 38 | 39 | /* If you declare any globals in php_jsonrpc.h uncomment this: 40 | ZEND_DECLARE_MODULE_GLOBALS(jsonrpc) 41 | */ 42 | 43 | /* True global resources - no need for thread safety here */ 44 | 45 | 46 | /* {{{ jsonrpc_functions[] 47 | * 48 | * Every user visible function must have an entry in jsonrpc_functions[]. 49 | */ 50 | const zend_function_entry jsonrpc_functions[] = { 51 | PHP_FE(confirm_jsonrpc_compiled, NULL) /* For testing, remove later. */ 52 | PHP_FE_END /* Must be the last line in jsonrpc_functions[] */ 53 | }; 54 | /* }}} */ 55 | 56 | /* {{{ jsonrpc_module_entry 57 | */ 58 | zend_module_entry jsonrpc_module_entry = { 59 | #if ZEND_MODULE_API_NO >= 20010901 60 | STANDARD_MODULE_HEADER, 61 | #endif 62 | "jsonrpc", 63 | jsonrpc_functions, 64 | PHP_MINIT(jsonrpc), 65 | PHP_MSHUTDOWN(jsonrpc), 66 | PHP_RINIT(jsonrpc), /* Replace with NULL if there's nothing to do at request start */ 67 | PHP_RSHUTDOWN(jsonrpc), /* Replace with NULL if there's nothing to do at request end */ 68 | PHP_MINFO(jsonrpc), 69 | #if ZEND_MODULE_API_NO >= 20010901 70 | "0.1", /* Replace with version number for your extension */ 71 | #endif 72 | STANDARD_MODULE_PROPERTIES 73 | }; 74 | /* }}} */ 75 | 76 | #ifdef COMPILE_DL_JSONRPC 77 | ZEND_GET_MODULE(jsonrpc) 78 | #endif 79 | 80 | /* {{{ PHP_INI 81 | */ 82 | /* Remove comments and fill if you need to have entries in php.ini 83 | PHP_INI_BEGIN() 84 | STD_PHP_INI_ENTRY("jsonrpc.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_jsonrpc_globals, jsonrpc_globals) 85 | STD_PHP_INI_ENTRY("jsonrpc.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_jsonrpc_globals, jsonrpc_globals) 86 | PHP_INI_END() 87 | */ 88 | /* }}} */ 89 | 90 | /* {{{ php_jsonrpc_init_globals 91 | */ 92 | /* Uncomment this function if you have INI entries 93 | static void php_jsonrpc_init_globals(zend_jsonrpc_globals *jsonrpc_globals) 94 | { 95 | jsonrpc_globals->global_value = 0; 96 | jsonrpc_globals->global_string = NULL; 97 | } 98 | */ 99 | /* }}} */ 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | /* {{{ PHP_MINIT_FUNCTION 110 | */ 111 | PHP_MINIT_FUNCTION(jsonrpc) 112 | { 113 | //le_curl = zend_register_list_destructors_ex(_php_curl_close, NULL, "curl", module_number); 114 | 115 | //le_curl_multi_handle = zend_register_list_destructors_ex(NULL, _php_curlm_close_persistent, "curl_multi", module_number); 116 | /* If you have INI entries, uncomment these lines 117 | REGISTER_INI_ENTRIES(); 118 | */ 119 | 120 | /*zend_class_entry jsonrpc_server_class_entry; 121 | INIT_CLASS_ENTRY(jsonrpc_server_class_entry, "Jsonrpc_Server", jsonrpc_server_class_functions); 122 | php_jsonrpc_server_entry = zend_register_internal_class(&jsonrpc_server_class_entry TSRMLS_CC); 123 | */ 124 | /*zend_class_entry jsonrpc_client_class_entry; 125 | INIT_CLASS_ENTRY(jsonrpc_client_class_entry, "Jsonrpc_Client", jsonrpc_client_class_functions); 126 | php_jsonrpc_client_entry = zend_register_internal_class(&jsonrpc_client_class_entry TSRMLS_CC); 127 | */ 128 | jsonrpc_yajl_init(module_number TSRMLS_CC); 129 | 130 | jsonrpc_client_init(module_number TSRMLS_CC); 131 | 132 | jsonrpc_server_init(module_number TSRMLS_CC); 133 | 134 | return SUCCESS; 135 | } 136 | /* }}} */ 137 | 138 | /* {{{ PHP_MSHUTDOWN_FUNCTION 139 | */ 140 | PHP_MSHUTDOWN_FUNCTION(jsonrpc) 141 | { 142 | /* uncomment this line if you have INI entries 143 | UNREGISTER_INI_ENTRIES(); 144 | */ 145 | return SUCCESS; 146 | } 147 | /* }}} */ 148 | 149 | /* Remove if there's nothing to do at request start */ 150 | /* {{{ PHP_RINIT_FUNCTION 151 | */ 152 | PHP_RINIT_FUNCTION(jsonrpc) 153 | { 154 | return SUCCESS; 155 | } 156 | /* }}} */ 157 | 158 | /* Remove if there's nothing to do at request end */ 159 | /* {{{ PHP_RSHUTDOWN_FUNCTION 160 | */ 161 | PHP_RSHUTDOWN_FUNCTION(jsonrpc) 162 | { 163 | return SUCCESS; 164 | } 165 | /* }}} */ 166 | 167 | /* {{{ PHP_MINFO_FUNCTION 168 | */ 169 | PHP_MINFO_FUNCTION(jsonrpc) 170 | { 171 | php_info_print_table_start(); 172 | php_info_print_table_row(2, "php jsonrpc support", "enabled"); 173 | php_info_print_table_row(2, "php jsonrpc version", PHP_JSONRPC_VERSION); 174 | php_info_print_table_end(); 175 | 176 | /* Remove comments if you have entries in php.ini 177 | DISPLAY_INI_ENTRIES(); 178 | */ 179 | } 180 | /* }}} */ 181 | 182 | 183 | /* Remove the following function when you have succesfully modified config.m4 184 | so that your module can be compiled into PHP, it exists only for testing 185 | purposes. */ 186 | 187 | /* Every user-visible function in PHP should document itself in the source */ 188 | /* {{{ proto string confirm_jsonrpc_compiled(string arg) 189 | Return a string to confirm that the module is compiled in */ 190 | PHP_FUNCTION(confirm_jsonrpc_compiled) 191 | { 192 | char *arg = NULL; 193 | int arg_len, len; 194 | char *strg; 195 | 196 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { 197 | return; 198 | } 199 | 200 | len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "jsonrpc", arg); 201 | RETURN_STRINGL(strg, len, 0); 202 | } 203 | /* }}} */ 204 | /* The previous line is meant for vim and emacs, so it can correctly fold and 205 | unfold functions in source code. See the corresponding marks just before 206 | function definition, where the functions purpose is also documented. Please 207 | follow this convention for the convenience of others editing your code. 208 | */ 209 | 210 | 211 | /* 212 | * Local variables: 213 | * tab-width: 4 214 | * c-basic-offset: 4 215 | * End: 216 | * vim600: noet sw=4 ts=4 fdm=marker 217 | * vim<600: noet sw=4 ts=4 218 | */ 219 | -------------------------------------------------------------------------------- /yajl/api/yajl_gen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /** 18 | * \file yajl_gen.h 19 | * Interface to YAJL's JSON generation facilities. 20 | */ 21 | 22 | #include "yajl_common.h" 23 | 24 | #ifndef __YAJL_GEN_H__ 25 | #define __YAJL_GEN_H__ 26 | 27 | #include 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | /** generator status codes */ 33 | typedef enum { 34 | /** no error */ 35 | yajl_gen_status_ok = 0, 36 | /** at a point where a map key is generated, a function other than 37 | * yajl_gen_string was called */ 38 | yajl_gen_keys_must_be_strings, 39 | /** YAJL's maximum generation depth was exceeded. see 40 | * YAJL_MAX_DEPTH */ 41 | yajl_max_depth_exceeded, 42 | /** A generator function (yajl_gen_XXX) was called while in an error 43 | * state */ 44 | yajl_gen_in_error_state, 45 | /** A complete JSON document has been generated */ 46 | yajl_gen_generation_complete, 47 | /** yajl_gen_double was passed an invalid floating point value 48 | * (infinity or NaN). */ 49 | yajl_gen_invalid_number, 50 | /** A print callback was passed in, so there is no internal 51 | * buffer to get from */ 52 | yajl_gen_no_buf, 53 | /** returned from yajl_gen_string() when the yajl_gen_validate_utf8 54 | * option is enabled and an invalid was passed by client code. 55 | */ 56 | yajl_gen_invalid_string 57 | } yajl_gen_status; 58 | 59 | /** an opaque handle to a generator */ 60 | typedef struct yajl_gen_t * yajl_gen; 61 | 62 | /** a callback used for "printing" the results. */ 63 | typedef void (*yajl_print_t)(void * ctx, 64 | const char * str, 65 | size_t len); 66 | 67 | /** configuration parameters for the parser, these may be passed to 68 | * yajl_gen_config() along with option specific argument(s). In general, 69 | * all configuration parameters default to *off*. */ 70 | typedef enum { 71 | /** generate indented (beautiful) output */ 72 | yajl_gen_beautify = 0x01, 73 | /** 74 | * Set an indent string which is used when yajl_gen_beautify 75 | * is enabled. Maybe something like \\t or some number of 76 | * spaces. The default is four spaces ' '. 77 | */ 78 | yajl_gen_indent_string = 0x02, 79 | /** 80 | * Set a function and context argument that should be used to 81 | * output generated json. the function should conform to the 82 | * yajl_print_t prototype while the context argument is a 83 | * void * of your choosing. 84 | * 85 | * example: 86 | * yajl_gen_config(g, yajl_gen_print_callback, myFunc, myVoidPtr); 87 | */ 88 | yajl_gen_print_callback = 0x04, 89 | /** 90 | * Normally the generator does not validate that strings you 91 | * pass to it via yajl_gen_string() are valid UTF8. Enabling 92 | * this option will cause it to do so. 93 | */ 94 | yajl_gen_validate_utf8 = 0x08, 95 | /** 96 | * the forward solidus (slash or '/' in human) is not required to be 97 | * escaped in json text. By default, YAJL will not escape it in the 98 | * iterest of saving bytes. Setting this flag will cause YAJL to 99 | * always escape '/' in generated JSON strings. 100 | */ 101 | yajl_gen_escape_solidus = 0x10 102 | } yajl_gen_option; 103 | 104 | /** allow the modification of generator options subsequent to handle 105 | * allocation (via yajl_alloc) 106 | * \returns zero in case of errors, non-zero otherwise 107 | */ 108 | YAJL_API int yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...); 109 | 110 | /** allocate a generator handle 111 | * \param allocFuncs an optional pointer to a structure which allows 112 | * the client to overide the memory allocation 113 | * used by yajl. May be NULL, in which case 114 | * malloc/free/realloc will be used. 115 | * 116 | * \returns an allocated handle on success, NULL on failure (bad params) 117 | */ 118 | YAJL_API yajl_gen yajl_gen_alloc(const yajl_alloc_funcs * allocFuncs); 119 | 120 | /** free a generator handle */ 121 | YAJL_API void yajl_gen_free(yajl_gen handle); 122 | 123 | YAJL_API yajl_gen_status yajl_gen_integer(yajl_gen hand, long long int number); 124 | /** generate a floating point number. number may not be infinity or 125 | * NaN, as these have no representation in JSON. In these cases the 126 | * generator will return 'yajl_gen_invalid_number' */ 127 | YAJL_API yajl_gen_status yajl_gen_double(yajl_gen hand, double number); 128 | YAJL_API yajl_gen_status yajl_gen_number(yajl_gen hand, 129 | const char * num, 130 | size_t len); 131 | YAJL_API yajl_gen_status yajl_gen_string(yajl_gen hand, 132 | const unsigned char * str, 133 | size_t len); 134 | YAJL_API yajl_gen_status yajl_gen_null(yajl_gen hand); 135 | YAJL_API yajl_gen_status yajl_gen_bool(yajl_gen hand, int boolean); 136 | YAJL_API yajl_gen_status yajl_gen_map_open(yajl_gen hand); 137 | YAJL_API yajl_gen_status yajl_gen_map_close(yajl_gen hand); 138 | YAJL_API yajl_gen_status yajl_gen_array_open(yajl_gen hand); 139 | YAJL_API yajl_gen_status yajl_gen_array_close(yajl_gen hand); 140 | 141 | /** access the null terminated generator buffer. If incrementally 142 | * outputing JSON, one should call yajl_gen_clear to clear the 143 | * buffer. This allows stream generation. */ 144 | YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand, 145 | const unsigned char ** buf, 146 | size_t * len); 147 | 148 | /** clear yajl's output buffer, but maintain all internal generation 149 | * state. This function will not "reset" the generator state, and is 150 | * intended to enable incremental JSON outputing. */ 151 | YAJL_API void yajl_gen_clear(yajl_gen hand); 152 | 153 | /** Reset the generator state. Allows a client to generate multiple 154 | * json entities in a stream. The "sep" string will be inserted to 155 | * separate the previously generated entity from the current, 156 | * NULL means *no separation* of entites (clients beware, generating 157 | * multiple JSON numbers, for instance, will result in inscrutable 158 | * output) */ 159 | YAJL_API void yajl_gen_reset(yajl_gen hand, const char * sep); 160 | 161 | #ifdef __cplusplus 162 | } 163 | #endif 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /yajl/api/yajl_tree.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010-2011 Florian Forster 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /** 18 | * \file yajl_tree.h 19 | * 20 | * Parses JSON data and returns the data in tree form. 21 | * 22 | * \author Florian Forster 23 | * \date August 2010 24 | * 25 | * This interface makes quick parsing and extraction of 26 | * smallish JSON docs trivial: 27 | * 28 | * \include example/parse_config.c 29 | */ 30 | 31 | #ifndef YAJL_TREE_H 32 | #define YAJL_TREE_H 1 33 | 34 | #include "yajl_common.h" 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | /** possible data types that a yajl_val_s can hold */ 41 | typedef enum { 42 | yajl_t_string = 1, 43 | yajl_t_number = 2, 44 | yajl_t_object = 3, 45 | yajl_t_array = 4, 46 | yajl_t_true = 5, 47 | yajl_t_false = 6, 48 | yajl_t_null = 7, 49 | /** The any type isn't valid for yajl_val_s.type, but can be 50 | * used as an argument to routines like yajl_tree_get(). 51 | */ 52 | yajl_t_any = 8 53 | } yajl_type; 54 | 55 | #define YAJL_NUMBER_INT_VALID 0x01 56 | #define YAJL_NUMBER_DOUBLE_VALID 0x02 57 | 58 | /** A pointer to a node in the parse tree */ 59 | typedef struct yajl_val_s * yajl_val; 60 | 61 | /** 62 | * A JSON value representation capable of holding one of the seven 63 | * types above. For "string", "number", "object", and "array" 64 | * additional data is available in the union. The "YAJL_IS_*" 65 | * and "YAJL_GET_*" macros below allow type checking and convenient 66 | * value extraction. 67 | */ 68 | struct yajl_val_s 69 | { 70 | /** Type of the value contained. Use the "YAJL_IS_*" macros to check for a 71 | * specific type. */ 72 | yajl_type type; 73 | /** Type-specific data. You may use the "YAJL_GET_*" macros to access these 74 | * members. */ 75 | union 76 | { 77 | char * string; 78 | struct { 79 | long long i; /*< integer value, if representable. */ 80 | double d; /*< double value, if representable. */ 81 | char *r; /*< unparsed number in string form. */ 82 | /** Signals whether the \em i and \em d members are 83 | * valid. See \c YAJL_NUMBER_INT_VALID and 84 | * \c YAJL_NUMBER_DOUBLE_VALID. */ 85 | unsigned int flags; 86 | } number; 87 | struct { 88 | const char **keys; /*< Array of keys */ 89 | yajl_val *values; /*< Array of values. */ 90 | size_t len; /*< Number of key-value-pairs. */ 91 | } object; 92 | struct { 93 | yajl_val *values; /*< Array of elements. */ 94 | size_t len; /*< Number of elements. */ 95 | } array; 96 | } u; 97 | }; 98 | 99 | /** 100 | * Parse a string. 101 | * 102 | * Parses an null-terminated string containing JSON data and returns a pointer 103 | * to the top-level value (root of the parse tree). 104 | * 105 | * \param input Pointer to a null-terminated utf8 string containing 106 | * JSON data. 107 | * \param error_buffer Pointer to a buffer in which an error message will 108 | * be stored if \em yajl_tree_parse fails, or 109 | * \c NULL. The buffer will be initialized before 110 | * parsing, so its content will be destroyed even if 111 | * \em yajl_tree_parse succeeds. 112 | * \param error_buffer_size Size of the memory area pointed to by 113 | * \em error_buffer_size. If \em error_buffer_size is 114 | * \c NULL, this argument is ignored. 115 | * 116 | * \returns Pointer to the top-level value or \c NULL on error. The memory 117 | * pointed to must be freed using \em yajl_tree_free. In case of an error, a 118 | * null terminated message describing the error in more detail is stored in 119 | * \em error_buffer if it is not \c NULL. 120 | */ 121 | YAJL_API yajl_val yajl_tree_parse (const char *input, 122 | char *error_buffer, size_t error_buffer_size); 123 | 124 | 125 | /** 126 | * Free a parse tree returned by "yajl_tree_parse". 127 | * 128 | * \param v Pointer to a JSON value returned by "yajl_tree_parse". Passing NULL 129 | * is valid and results in a no-op. 130 | */ 131 | YAJL_API void yajl_tree_free (yajl_val v); 132 | 133 | /** 134 | * Access a nested value inside a tree. 135 | * 136 | * \param parent the node under which you'd like to extract values. 137 | * \param path A null terminated array of strings, each the name of an object key 138 | * \param type the yajl_type of the object you seek, or yajl_t_any if any will do. 139 | * 140 | * \returns a pointer to the found value, or NULL if we came up empty. 141 | * 142 | * Future Ideas: it'd be nice to move path to a string and implement support for 143 | * a teeny tiny micro language here, so you can extract array elements, do things 144 | * like .first and .last, even .length. Inspiration from JSONPath and css selectors? 145 | * No it wouldn't be fast, but that's not what this API is about. 146 | */ 147 | YAJL_API yajl_val yajl_tree_get(yajl_val parent, const char ** path, yajl_type type); 148 | 149 | /* Various convenience macros to check the type of a `yajl_val` */ 150 | #define YAJL_IS_STRING(v) (((v) != NULL) && ((v)->type == yajl_t_string)) 151 | #define YAJL_IS_NUMBER(v) (((v) != NULL) && ((v)->type == yajl_t_number)) 152 | #define YAJL_IS_INTEGER(v) (YAJL_IS_NUMBER(v) && ((v)->u.number.flags & YAJL_NUMBER_INT_VALID)) 153 | #define YAJL_IS_DOUBLE(v) (YAJL_IS_NUMBER(v) && ((v)->u.number.flags & YAJL_NUMBER_DOUBLE_VALID)) 154 | #define YAJL_IS_OBJECT(v) (((v) != NULL) && ((v)->type == yajl_t_object)) 155 | #define YAJL_IS_ARRAY(v) (((v) != NULL) && ((v)->type == yajl_t_array )) 156 | #define YAJL_IS_TRUE(v) (((v) != NULL) && ((v)->type == yajl_t_true )) 157 | #define YAJL_IS_FALSE(v) (((v) != NULL) && ((v)->type == yajl_t_false )) 158 | #define YAJL_IS_NULL(v) (((v) != NULL) && ((v)->type == yajl_t_null )) 159 | 160 | /** Given a yajl_val_string return a ptr to the bare string it contains, 161 | * or NULL if the value is not a string. */ 162 | #define YAJL_GET_STRING(v) (YAJL_IS_STRING(v) ? (v)->u.string : NULL) 163 | 164 | /** Get the string representation of a number. You should check type first, 165 | * perhaps using YAJL_IS_NUMBER */ 166 | #define YAJL_GET_NUMBER(v) ((v)->u.number.r) 167 | 168 | /** Get the double representation of a number. You should check type first, 169 | * perhaps using YAJL_IS_DOUBLE */ 170 | #define YAJL_GET_DOUBLE(v) ((v)->u.number.d) 171 | 172 | /** Get the 64bit (long long) integer representation of a number. You should 173 | * check type first, perhaps using YAJL_IS_INTEGER */ 174 | #define YAJL_GET_INTEGER(v) ((v)->u.number.i) 175 | 176 | /** Get a pointer to a yajl_val_object or NULL if the value is not an object. */ 177 | #define YAJL_GET_OBJECT(v) (YAJL_IS_OBJECT(v) ? &(v)->u.object : NULL) 178 | 179 | /** Get a pointer to a yajl_val_array or NULL if the value is not an object. */ 180 | #define YAJL_GET_ARRAY(v) (YAJL_IS_ARRAY(v) ? &(v)->u.array : NULL) 181 | 182 | #ifdef __cplusplus 183 | } 184 | #endif 185 | 186 | #endif /* YAJL_TREE_H */ 187 | -------------------------------------------------------------------------------- /yajl/yajl_encode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "yajl_encode.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | static void CharToHex(unsigned char c, char * hexBuf) 25 | { 26 | const char * hexchar = "0123456789ABCDEF"; 27 | hexBuf[0] = hexchar[c >> 4]; 28 | hexBuf[1] = hexchar[c & 0x0F]; 29 | } 30 | 31 | void 32 | yajl_string_encode(const yajl_print_t print, 33 | void * ctx, 34 | const unsigned char * str, 35 | size_t len, 36 | int escape_solidus) 37 | { 38 | size_t beg = 0; 39 | size_t end = 0; 40 | char hexBuf[7]; 41 | hexBuf[0] = '\\'; hexBuf[1] = 'u'; hexBuf[2] = '0'; hexBuf[3] = '0'; 42 | hexBuf[6] = 0; 43 | 44 | while (end < len) { 45 | const char * escaped = NULL; 46 | switch (str[end]) { 47 | case '\r': escaped = "\\r"; break; 48 | case '\n': escaped = "\\n"; break; 49 | case '\\': escaped = "\\\\"; break; 50 | /* it is not required to escape a solidus in JSON: 51 | * read sec. 2.5: http://www.ietf.org/rfc/rfc4627.txt 52 | * specifically, this production from the grammar: 53 | * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF 54 | */ 55 | case '/': if (escape_solidus) escaped = "\\/"; break; 56 | case '"': escaped = "\\\""; break; 57 | case '\f': escaped = "\\f"; break; 58 | case '\b': escaped = "\\b"; break; 59 | case '\t': escaped = "\\t"; break; 60 | default: 61 | if ((unsigned char) str[end] < 32) { 62 | CharToHex(str[end], hexBuf + 4); 63 | escaped = hexBuf; 64 | } 65 | break; 66 | } 67 | if (escaped != NULL) { 68 | print(ctx, (const char *) (str + beg), end - beg); 69 | print(ctx, escaped, (unsigned int)strlen(escaped)); 70 | beg = ++end; 71 | } else { 72 | ++end; 73 | } 74 | } 75 | print(ctx, (const char *) (str + beg), end - beg); 76 | } 77 | 78 | static void hexToDigit(unsigned int * val, const unsigned char * hex) 79 | { 80 | unsigned int i; 81 | for (i=0;i<4;i++) { 82 | unsigned char c = hex[i]; 83 | if (c >= 'A') c = (c & ~0x20) - 7; 84 | c -= '0'; 85 | assert(!(c & 0xF0)); 86 | *val = (*val << 4) | c; 87 | } 88 | } 89 | 90 | static void Utf32toUtf8(unsigned int codepoint, char * utf8Buf) 91 | { 92 | if (codepoint < 0x80) { 93 | utf8Buf[0] = (char) codepoint; 94 | utf8Buf[1] = 0; 95 | } else if (codepoint < 0x0800) { 96 | utf8Buf[0] = (char) ((codepoint >> 6) | 0xC0); 97 | utf8Buf[1] = (char) ((codepoint & 0x3F) | 0x80); 98 | utf8Buf[2] = 0; 99 | } else if (codepoint < 0x10000) { 100 | utf8Buf[0] = (char) ((codepoint >> 12) | 0xE0); 101 | utf8Buf[1] = (char) (((codepoint >> 6) & 0x3F) | 0x80); 102 | utf8Buf[2] = (char) ((codepoint & 0x3F) | 0x80); 103 | utf8Buf[3] = 0; 104 | } else if (codepoint < 0x200000) { 105 | utf8Buf[0] =(char)((codepoint >> 18) | 0xF0); 106 | utf8Buf[1] =(char)(((codepoint >> 12) & 0x3F) | 0x80); 107 | utf8Buf[2] =(char)(((codepoint >> 6) & 0x3F) | 0x80); 108 | utf8Buf[3] =(char)((codepoint & 0x3F) | 0x80); 109 | utf8Buf[4] = 0; 110 | } else { 111 | utf8Buf[0] = '?'; 112 | utf8Buf[1] = 0; 113 | } 114 | } 115 | 116 | void yajl_string_decode(yajl_buf buf, const unsigned char * str, 117 | size_t len) 118 | { 119 | size_t beg = 0; 120 | size_t end = 0; 121 | 122 | while (end < len) { 123 | if (str[end] == '\\') { 124 | char utf8Buf[5]; 125 | const char * unescaped = "?"; 126 | yajl_buf_append(buf, str + beg, end - beg); 127 | switch (str[++end]) { 128 | case 'r': unescaped = "\r"; break; 129 | case 'n': unescaped = "\n"; break; 130 | case '\\': unescaped = "\\"; break; 131 | case '/': unescaped = "/"; break; 132 | case '"': unescaped = "\""; break; 133 | case 'f': unescaped = "\f"; break; 134 | case 'b': unescaped = "\b"; break; 135 | case 't': unescaped = "\t"; break; 136 | case 'u': { 137 | unsigned int codepoint = 0; 138 | hexToDigit(&codepoint, str + ++end); 139 | end+=3; 140 | /* check if this is a surrogate */ 141 | if ((codepoint & 0xFC00) == 0xD800) { 142 | end++; 143 | if (str[end] == '\\' && str[end + 1] == 'u') { 144 | unsigned int surrogate = 0; 145 | hexToDigit(&surrogate, str + end + 2); 146 | codepoint = 147 | (((codepoint & 0x3F) << 10) | 148 | ((((codepoint >> 6) & 0xF) + 1) << 16) | 149 | (surrogate & 0x3FF)); 150 | end += 5; 151 | } else { 152 | unescaped = "?"; 153 | break; 154 | } 155 | } 156 | 157 | Utf32toUtf8(codepoint, utf8Buf); 158 | unescaped = utf8Buf; 159 | 160 | if (codepoint == 0) { 161 | yajl_buf_append(buf, unescaped, 1); 162 | beg = ++end; 163 | continue; 164 | } 165 | 166 | break; 167 | } 168 | default: 169 | assert("this should never happen" == NULL); 170 | } 171 | yajl_buf_append(buf, unescaped, (unsigned int)strlen(unescaped)); 172 | beg = ++end; 173 | } else { 174 | end++; 175 | } 176 | } 177 | yajl_buf_append(buf, str + beg, end - beg); 178 | } 179 | 180 | #define ADV_PTR s++; if (!(len--)) return 0; 181 | 182 | int yajl_string_validate_utf8(const unsigned char * s, size_t len) 183 | { 184 | if (!len) return 1; 185 | if (!s) return 0; 186 | 187 | while (len--) { 188 | /* single byte */ 189 | if (*s <= 0x7f) { 190 | /* noop */ 191 | } 192 | /* two byte */ 193 | else if ((*s >> 5) == 0x6) { 194 | ADV_PTR; 195 | if (!((*s >> 6) == 0x2)) return 0; 196 | } 197 | /* three byte */ 198 | else if ((*s >> 4) == 0x0e) { 199 | ADV_PTR; 200 | if (!((*s >> 6) == 0x2)) return 0; 201 | ADV_PTR; 202 | if (!((*s >> 6) == 0x2)) return 0; 203 | } 204 | /* four byte */ 205 | else if ((*s >> 3) == 0x1e) { 206 | ADV_PTR; 207 | if (!((*s >> 6) == 0x2)) return 0; 208 | ADV_PTR; 209 | if (!((*s >> 6) == 0x2)) return 0; 210 | ADV_PTR; 211 | if (!((*s >> 6) == 0x2)) return 0; 212 | } else { 213 | return 0; 214 | } 215 | 216 | s++; 217 | } 218 | 219 | return 1; 220 | } 221 | -------------------------------------------------------------------------------- /yajl/ChangeLog: -------------------------------------------------------------------------------- 1 | 2.1.0 2 | * @nonodename, @patperry - fixed some compiler warnings 3 | * @yep, @emaste - documentation improvements 4 | * @sgravrock - build fix for NetBSD (and whenever sh != bash) 5 | * @rotty, @brimstone3, @lloyd - allow client to reset generator 6 | * @sgravrock - remove bash dependencies 7 | * @lloyd - add api tests 8 | * @rflynn - remove ruby dependency 9 | * @cloderic - nmake install works on windows 10 | * @shahbag - build fix for qnx 11 | * @breese - debugging improvements 12 | * @lloyd - json_verify supports -s flag for stream processing 13 | * @lloyd - json_reformat supports -s flag for stream processing 14 | 15 | 2.0.4 16 | * @jcekstrom - additional checking in integer parsing 17 | * @jcekstrom - fix a bug in yajl_tree that would cause valid json integersto fail to parse 18 | * @plaguemorin - fix a memory leak in yajl_tree (error strings were being leaked) 19 | * @7AC - reset errno 20 | * @ConradIrwin - include flags to reformatter to allow toggling of escape solidus option 21 | 22 | 2.0.3 23 | * John Stamp generation of a pkgconfig file at build time. 24 | * @robzuber bugfix in yajl_tree_get() 25 | * @lloyd - fix for compilation on 64 bit windows 26 | 27 | 2.0.2 28 | * lth fix typos in yajl_tree.h macros YAJL_IS_INTEGER and YAJL_IS_DOUBLE, 29 | contributed by Artem S Vybornov. 30 | * lth add #ifdef __cplusplus wrappers to yajl_tree to allow proper 31 | usage from many populer C++ compilers. 32 | 33 | 2.0.1 34 | * lth generator flag to allow client to specify they want 35 | escaped solidi '/'. issue #28 36 | * lth crash fix when yajl_parse() is never called. issue #27 37 | 38 | 2.0.0 39 | * lth YAJL is now ISC licensed: http://en.wikipedia.org/wiki/ISC_license 40 | * lth 20-35% (osx and linux respectively) parsing performance 41 | improvement attained by tweaking string scanning (idea: @michaelrhanson). 42 | * Florian Forster & lth - yajl_tree interface introduced as a higher level 43 | interface to the parser (eats JSON, poops a memory representation) 44 | * lth require a C99 compiler 45 | * lth integers are now represented with long long (64bit+) on all platforms. 46 | * lth size_t now used throughout to represent buffer lengths, so you can 47 | safely manage buffers greater than 4GB. 48 | * gno semantic improvements to yajl's API regarding partial value parsing and 49 | trailing garbage 50 | * lth new configuration mechanism for yajl, see yajl_config() and 51 | yajl_gen_config() 52 | * gno more allocation checking in more places 53 | * gno remove usage of strtol, replace with custom implementation that cares 54 | not about your locale. 55 | * lth yajl_parse_complete renamed to yajl_complete_parse. 56 | * lth add a switch to validate utf8 strings as they are generated. 57 | * lth tests are a lot quieter in their output. 58 | * lth addition of a little in tree performance benchmark, `perftest` in 59 | perf/perftest.c 60 | 61 | 1.0.12 62 | * Conrad Irwin - Parse null bytes correctly 63 | * Mirek Rusin - fix LLVM warnings 64 | * gno - Don't generate numbers for keys. closes #13 65 | * lth - various win32 fixes, including build documentation improvements 66 | * John Stamp - Don't export private symbols. 67 | * John Stamp - Install yajl_version.h, not the template. 68 | * John Stamp - Don't use -fPIC for static lib. Cmake will automatically add it for the shared. 69 | * lth 0 fix paths embedded in dylib upon installation on osx. closes #11 70 | 71 | 1.0.11 72 | * lth remove -Wno-missing-field-initializers for greater gcc compat (3.4.6) 73 | 74 | 1.0.10 75 | * Brian Maher - yajl is now buildable without a c++ compiler present 76 | * Brian Maher - fix header installation on OSX with cmake 2.8.0 installed 77 | * lth & vitali - allow builder to specify alternate lib directory 78 | for installation (i.e. lib64) 79 | * Vitali Lovich - yajl version number now programatically accessible 80 | * lth - prevent cmake from embedding rpaths in binaries. Static linking 81 | makes this unneccesary. 82 | 83 | 1.0.9 84 | * lth - fix inverted logic causing yajl_gen_double() to always fail on 85 | win32 (thanks to Fredrik Kihlander for the report) 86 | 87 | 1.0.8 88 | * Randall E. Barker - move dllexport defnitions so dlls with proper 89 | exports can again be generated on windows 90 | * lth - add yajl_get_bytes_consumed() which allows the client to 91 | determine the offset as an error, as well as determine how 92 | many bytes of an input buffer were consumed. 93 | * lth - fixes to keep "error offset" up to date (like when the 94 | client callback returns 0) 95 | * Brian Maher - allow client to specify a printing function in 96 | generation 97 | 98 | 1.0.7 99 | * lth fix win32 build (isinf and isnan) 100 | 101 | 1.0.6 102 | * lth fix several compiler warnings 103 | * lth fix generation of invalid json from yajl_gen_double 104 | (NaN is not JSON) 105 | * jstamp support for combining short options in tools 106 | * jstamp exit properly on errors from tools 107 | * octo test success no longer depends on integer size 108 | * max fix configure --prefix 109 | 110 | 1.0.5 111 | * lth several performance improvements related to function 112 | inlinin' 113 | 114 | 1.0.4 115 | * lth fix broken utf8 validation for three & four byte represenations. 116 | thanks to http://github.com/brianmario and 117 | http://github.com/technoweenie 118 | 119 | 1.0.3 120 | * lth fix syntax error in cplusplus extern "C" statements for wider 121 | compiler support 122 | 123 | 1.0.2 124 | * lth update doxygen documentation with new sample code, passing NULL 125 | for allocation functions added in 1.0.0 126 | 127 | 1.0.1 128 | * lth resolve crash in json_reformatter due to incorrectly ordered 129 | parameters. 130 | 131 | 1.0.0 132 | * lth add 'make install' rules, thaks to Andrei Soroker for the 133 | contribution. 134 | * lth client may override allocation routines at generator or parser 135 | allocation time 136 | * tjw add yajl_parse_complete routine to allow client to explicitly 137 | specify end-of-input, solving the "lonely number" case, where 138 | json text consists only of an element with no explicit syntactic 139 | end. 140 | * tjw many new test cases 141 | * tjw cleanup of code for symmetry and ease of reading 142 | * lth integration of patches from Robert Varga which cleanup 143 | compilation warnings on 64 bit linux 144 | 145 | 0.4.0 146 | * lth buffer overflow bug in yajl_gen_double s/%lf/%g/ - thanks to 147 | Eric Bergstrome 148 | * lth yajl_number callback to allow passthrough of arbitrary precision 149 | numbers to client. Thanks to Hatem Nassrat. 150 | * lth yajl_integer now deals in long, instead of long long. This 151 | combined with yajl_number improves compiler compatibility while 152 | maintaining precision. 153 | * lth better ./configure && make experience (still requires cmake and 154 | ruby) 155 | * lth fix handling of special characters hex 0F and 1F in yajl_encode 156 | (thanks to Robert Geiger) 157 | * lth allow leading zeros in exponents (thanks to Hatem Nassrat) 158 | 159 | 0.3.0 160 | * lth doxygen documentation (html & man) generated as part of the 161 | build 162 | * lth many documentation updates. 163 | * lth fix to work with older versions of cmake (don't use LOOSE_LOOP 164 | constructs) 165 | * lth work around different behavior of freebsd 4 scanf. initialize 166 | parameter to scanf to zero. 167 | * lth all tests run 32x with ranging buffer sizes to stress stream 168 | parsing 169 | * lth yajl_test accepts -b option to allow read buffer size to be 170 | set 171 | * lth option to validate UTF8 added to parser (argument in 172 | yajl_parser_cfg) 173 | * lth fix buffer overrun when chunk ends inside \u escaped text 174 | * lth support client cancelation 175 | 176 | 0.2.2 177 | * lth on windows build debug with C7 symbols and no pdb files. 178 | 179 | 0.2.1 180 | * fix yajl_reformat and yajl_verify to work on arbitrarily sized 181 | inputs. 182 | * fix win32 build break, clean up all errors and warnings. 183 | * fix optimized build flags. 184 | 185 | 0.2.0 186 | * optionally support comments in input text 187 | 188 | 0.1.0 189 | * Initial release 190 | -------------------------------------------------------------------------------- /README-cn.md: -------------------------------------------------------------------------------- 1 | JsonRPC 2.0 Client and Server 2 | ============================= 3 | 4 | [![Build Status](https://travis-ci.org/rryqszq4/php-JsonRPC.svg?branch=master)](https://travis-ci.org/rryqszq4/php-JsonRPC) 5 | 6 | 轻量级,高性能 JsonRPC 2.0 客户端和服务端的php扩展,基于 multi_curl + epoll的并行客户端。Jsonrpc_Client使用libcurl库的并行接口调取服务,使用IO多路复用的epoll去监听curl的IO事件,使用协程可以同步返回rpc的数据,但底层其实是异步的。Jsonrpc_Server支持php-fpm或swoole。遵守[http://www.jsonrpc.org/](http://www.jsonrpc.org/)协议规范。 7 | [English](https://github.com/rryqszq4/JsonRPC/blob/master/README.md) 8 | 9 | [jsonrpc in php7](https://github.com/rryqszq4/php7-ext-jsonrpc) 10 | 11 | 特性 12 | ----------- 13 | * JSON-RPC 2.0协议规范 14 | * 并发curl与epoll结合的并行客户端 15 | * php-fpm中持久化epoll 16 | * php-fpm中持久化curl_multi队列 17 | * 默认使用YAJL解析JSON 18 | * 服务端支持请求与通知 19 | * Linux系统(需要支持epoll) 20 | 21 | PHP环境 22 | ----------- 23 | - PHP 5.3.* 24 | - PHP 5.4.* 25 | - PHP 5.5.* 26 | - PHP 5.6.* 27 | 28 | 安装 29 | ----------- 30 | ``` 31 | $/path/to/phpize 32 | $./configure --with-php-config=/path/to/php-config 33 | $make && make install 34 | ``` 35 | 36 | 服务端 37 | ----------- 38 | **接口** 39 | - Jsonrpc_Server::__construct(mixed $payload, array $callbacks, array $classes) 40 | - Jsonrpc_Server::register(string $name, mixed $closure) 41 | - Jsonrpc_Server::bind(string $procedure, mixed $classname, string $method) 42 | - Jsonrpc_Server::jsonformat() 43 | - Jsonrpc_Server::rpcformat(mixed $payload) 44 | - Jsonrpc_Server::executeprocedure(string $procedure, array $params) 45 | - Jsonrpc_Server::executecallback(object $closure, array $params) 46 | - Jsonrpc_Server::executemethod(string $class, string $method, array $params) 47 | - Jsonrpc_Server::execute(boolean $response_type) 48 | 49 | **注册函数** 50 | ```php 51 | register('addition1', $add1); 60 | 61 | // style two function string 62 | function add2($a, $b){ 63 | return $a + $b; 64 | } 65 | $server->register('addition2', 'add2'); 66 | 67 | // style three function closure 68 | $server->register('addition3', function ($a, $b) { 69 | return $a + $b; 70 | }); 71 | 72 | //style four class method string 73 | class Api 74 | { 75 | static public function add($a, $b) 76 | { 77 | return $a + $b; 78 | } 79 | } 80 | $server->register('addition4', 'Api::add'); 81 | 82 | echo $server->execute(); 83 | 84 | //output >>> 85 | //{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error"}} 86 | 87 | ?> 88 | ``` 89 | 90 | **绑定方法** 91 | ```php 92 | bind('addition5', 'Api', 'add'); 109 | 110 | $server->bind('addition6', $a=new Api, 'newadd'); 111 | 112 | echo $server->execute(); 113 | 114 | //output >>> 115 | //{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error"}} 116 | 117 | ?> 118 | 119 | ``` 120 | 121 | **swoole jsonrpc server** 122 | ```php 123 | on('Request', function($request, $response){ 132 | if ($request->server['request_uri'] == "/jsonrpc_server"){ 133 | $payload = $request->rawContent(); 134 | 135 | $jsr_server = new Jsonrpc_Server($payload); 136 | $jsr_server->register('addition', 'add'); 137 | $res = $jsr_server->execute(); 138 | $response->end($res); 139 | 140 | unset($payload); 141 | unset($jsr_server); 142 | unset($res); 143 | }else { 144 | $response->end("error"); 145 | } 146 | }); 147 | $http->start(); 148 | ?> 149 | ``` 150 | 151 | 客户端 152 | ------------ 153 | **接口** 154 | - Jsonrpc_Client::__construct(boolean $persist) 155 | - Jsonrpc_Client::call(string $url, string $procedure, array $params, mixed $id) 156 | - Jsonrpc_Client::connect(string $url) 157 | - Jsonrpc_Client::__call(string $procedure, array $params) 158 | - Jsonrpc_Client::execute(boolean $response_type) 159 | - Jsonrpc_Client::authentication(string $username, string $password) 160 | - Jsonrpc_Client::__destruct() 161 | 162 | **持久化** 163 | > Jsonrpc_client(1) 参数为1的时候,将epoll和curl_multi队列两个资源进行持久化,默认使用非持久化。 164 | 165 | **直接调用** 166 | ```php 167 | connect('http://localhost/server.php'); 171 | $client->addition1(3,5); 172 | $result = $client->execute(); 173 | 174 | ?> 175 | ``` 176 | 177 | **并行调用** 178 | ```php 179 | call('http://localhost/server.php', 'addition1', array(3,5)); 183 | $client->call('http://localhost/server.php', 'addition2', array(10,20)); 184 | 185 | /* ... */ 186 | $result = $client->execute(); 187 | 188 | var_dump($result); 189 | 190 | //output >>> 191 | /* 192 | array(2) { 193 | [0]=> 194 | array(3) { 195 | ["jsonrpc"]=> 196 | string(3) "2.0" 197 | ["id"]=> 198 | int(110507766) 199 | ["result"]=> 200 | int(8) 201 | } 202 | [1]=> 203 | array(3) { 204 | ["jsonrpc"]=> 205 | string(3) "2.0" 206 | ["id"]=> 207 | int(1559316299) 208 | ["result"]=> 209 | int(30) 210 | } 211 | ... 212 | } 213 | */ 214 | ?> 215 | ``` 216 | **自定义 id** 217 | ```php 218 | call('http://localhost/server.php', 'addition', array(3,5),"custom_id_001"); 222 | $result = $client->execute(); 223 | var_dump($result); 224 | 225 | //output >>> 226 | /* 227 | array(1) { 228 | [0]=> 229 | array(3) { 230 | ["jsonrpc"]=> 231 | string(3) "2.0" 232 | ["id"]=> 233 | string(13) "custom_id_001" 234 | ["result"]=> 235 | int(8) 236 | } 237 | } 238 | */ 239 | ?> 240 | ``` 241 | 242 | YAJL 生成/解析 243 | ------------------- 244 | **Interface** 245 | - Jsonrpc_Yajl::generate(array $array) 246 | - Jsonrpc_Yajl::parse(string $json) 247 | 248 | **生成** 249 | ```php 250 | "value") 256 | ); 257 | 258 | var_dump(Jsonrpc_Yajl::generate($arr)); 259 | 260 | /* ==>output 261 | string(28) "[1,"string",{"key":"value"}]"; 262 | */ 263 | 264 | ?> 265 | ``` 266 | 267 | **解析** 268 | ```php 269 | output 276 | array(3) { 277 | [0]=> 278 | int(1) 279 | [1]=> 280 | string(6) "string" 281 | [2]=> 282 | array(1) { 283 | ["key"]=> 284 | string(5) "value" 285 | } 286 | } 287 | */ 288 | 289 | ?> 290 | ``` 291 | 292 | 常见错误信息 293 | -------------- 294 | **jsonrpc 2.0 错误信息** 295 | ```javascript 296 | // 语法解析错误 297 | {"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error"}} 298 | 299 | // 无效请求 300 | {"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"Invalid Request"}} 301 | 302 | // 找不到方法 303 | {"jsonrpc":"2.0","id":null,"error":{"code":-32601,"message":"Method not found"}} 304 | 305 | // 无效的参数 306 | {"jsonrpc":"2.0","id":null,"error":{"code":-32602,"message":"Invalid params"}} 307 | 308 | // 309 | ``` 310 | 311 | **HTTP协议错误信息** 312 | ```javascript 313 | // 400 314 | {"jsonrpc":"2.0","id":null,"error":{"code":-32400,"message":"Bad Request"}} 315 | // 401 316 | {"jsonrpc":"2.0","id":null,"error":{"code":-32401,"message":"Unauthorized"}} 317 | // 403 318 | {"jsonrpc":"2.0","id":null,"error":{"code":-32403,"message":"Forbidden"}} 319 | // 404 320 | {"jsonrpc":"2.0","id":null,"error":{"code":-32404,"message":"Not Found"}} 321 | 322 | // 500 323 | {"jsonrpc":"2.0","id":null,"error":{"code":-32500,"message":"Internal Server Error"}} 324 | // 502 325 | {"jsonrpc":"2.0","id":null,"error":{"code":-32502,"message":"Bad Gateway"}} 326 | ... 327 | 328 | // unknow 329 | {"jsonrpc":"2.0","id":null,"error":{"code":-32599,"message":"HTTP Unknow"}} 330 | ``` 331 | 332 | **curl错误信息** 333 | ```javascript 334 | // 1 CURLE_UNSUPPORTED_PROTOCOL 335 | {"jsonrpc":"2.0","id":null,"error":{"code":-32001,"message":"Curl Unsupported Protocol"}} 336 | 337 | // 2 CURLE_FAILED_INIT 338 | {"jsonrpc":"2.0","id":null,"error":{"code":-32002,"message":"Curl Failed Init"}} 339 | 340 | // 3 CURLE_URL_MALFORMAT 341 | {"jsonrpc":"2.0","id":null,"error":{"code":-32003,"message":"Curl Url Malformat"}} 342 | 343 | // 4 344 | {"jsonrpc":"2.0","id":null,"error":{"code":-32004,"message":"Curl Not Built In"}} 345 | 346 | // 5 CURLE_COULDNT_RESOLVE_PROXY 347 | {"jsonrpc":"2.0","id":null,"error":{"code":-32005,"message":"Curl Couldnt Resolve Proxy"}} 348 | 349 | // 6 CURLE_COULDNT_RESOLVE_HOST 350 | {"jsonrpc":"2.0","id":null,"error":{"code":-32006,"message":"Curl Couldnt Resolve Host"}} 351 | 352 | // 7 CURLE_COULDNT_CONNECT 353 | {"jsonrpc":"2.0","id":null,"error":{"code":-32007,"message":"Curl Couldnt Connect"}} 354 | ... 355 | 356 | // CURL ERROR UNKNOW 357 | {"jsonrpc":"2.0","id":null,"error":{"code":-32099,"message":"Curl Error Unknow"}} 358 | ``` 359 | 360 | 361 | 362 | 363 | 364 | 365 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JsonRPC 2.0 Client and Server 2 | ============================= 3 | 4 | [![Build Status](https://travis-ci.org/rryqszq4/php-JsonRPC.svg?branch=master)](https://travis-ci.org/rryqszq4/php-JsonRPC) 5 | 6 | Lightweight, fast multi Json-RPC 2.0 client/server in php extension, base on multi_curl and epoll of the Client. Coroutine in async of writeback, just look like the sync of php response and it is. Jsonrpc_Server support php-fpm or swoole Compliance [http://www.jsonrpc.org/](http://www.jsonrpc.org/) protocol specification. [中文](https://github.com/rryqszq4/JsonRPC/blob/master/README-cn.md) 7 | 8 | [jsonrpc in php7](https://github.com/rryqszq4/php7-ext-jsonrpc) 9 | 10 | Features 11 | -------- 12 | * JSON-RPC 2.0 protocol 13 | * Base on curl and epoll of the multi client 14 | * Persistent epoll in php-fpm 15 | * Persistent curl_multi queue in php-fpm 16 | * Default use JSON parse/generate library YAJL 17 | * Support message and notification 18 | * Linux only(need to epoll) 19 | 20 | Requirement 21 | ----------- 22 | - PHP 5.3.* 23 | - PHP 5.4.* 24 | - PHP 5.5.* 25 | - PHP 5.6.* 26 | 27 | Install 28 | ------- 29 | ``` 30 | $/path/to/phpize 31 | $./configure --with-php-config=/path/to/php-config 32 | $make && make install 33 | ``` 34 | 35 | Server 36 | ----------- 37 | **Interface** 38 | - Jsonrpc_Server::__construct(mixed $payload, array $callbacks, array $classes) 39 | - Jsonrpc_Server::register(string $name, mixed $closure) 40 | - Jsonrpc_Server::bind(string $procedure, mixed $classname, string $method) 41 | - Jsonrpc_Server::jsonformat() 42 | - Jsonrpc_Server::rpcformat(mixed $payload) 43 | - Jsonrpc_Server::executeprocedure(string $procedure, array $params) 44 | - Jsonrpc_Server::executecallback(object $closure, array $params) 45 | - Jsonrpc_Server::executemethod(string $class, string $method, array $params) 46 | - Jsonrpc_Server::execute(boolean $response_type) 47 | 48 | **Register Function** 49 | ```php 50 | register('addition1', $add1); 59 | 60 | // style two function string 61 | function add2($a, $b){ 62 | return $a + $b; 63 | } 64 | $server->register('addition2', 'add2'); 65 | 66 | // style three function closure 67 | $server->register('addition3', function ($a, $b) { 68 | return $a + $b; 69 | }); 70 | 71 | //style four class method string 72 | class Api 73 | { 74 | static public function add($a, $b) 75 | { 76 | return $a + $b; 77 | } 78 | } 79 | $server->register('addition4', 'Api::add'); 80 | 81 | echo $server->execute(); 82 | 83 | //output >>> 84 | //{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error"}} 85 | 86 | ?> 87 | ``` 88 | 89 | **Bind Method** 90 | ```php 91 | bind('addition5', 'Api', 'add'); 108 | 109 | $server->bind('addition6', $a=new Api, 'newadd'); 110 | 111 | echo $server->execute(); 112 | 113 | //output >>> 114 | //{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error"}} 115 | 116 | ?> 117 | 118 | ``` 119 | 120 | **swoole jsonrpc server** 121 | ```php 122 | on('Request', function($request, $response){ 131 | if ($request->server['request_uri'] == "/jsonrpc_server"){ 132 | $payload = $request->rawContent(); 133 | 134 | $jsr_server = new Jsonrpc_Server($payload); 135 | $jsr_server->register('addition', 'add'); 136 | $res = $jsr_server->execute(); 137 | $response->end($res); 138 | 139 | unset($payload); 140 | unset($jsr_server); 141 | unset($res); 142 | }else { 143 | $response->end("error"); 144 | } 145 | }); 146 | $http->start(); 147 | ?> 148 | ``` 149 | 150 | Client 151 | ------------ 152 | **Interface** 153 | - Jsonrpc_Client::__construct(boolean $persist) 154 | - Jsonrpc_Client::call(string $url, string $procedure, array $params, mixed $id) 155 | - Jsonrpc_Client::connect(string $url) 156 | - Jsonrpc_Client::__call(string $procedure, array $params) 157 | - Jsonrpc_Client::execute(boolean $response_type) 158 | - Jsonrpc_Client::authentication(string $username, string $password) 159 | - Jsonrpc_Client::__destruct() 160 | 161 | **Persistent** 162 | > Jsonrpc_client(1) 163 | > When two resource epoll and curl_multi queue persist, the parame is 1. The default use of non-persistent. 164 | 165 | **Connect Call** 166 | ```php 167 | connect('http://localhost/server.php'); 171 | $client->addition1(3,5); 172 | $result = $client->execute(); 173 | 174 | ?> 175 | ``` 176 | 177 | **Multi Call** 178 | ```php 179 | call('http://localhost/server.php', 'addition1', array(3,5)); 183 | $client->call('http://localhost/server.php', 'addition2', array(10,20)); 184 | 185 | /* ... */ 186 | $result = $client->execute(); 187 | 188 | var_dump($result); 189 | 190 | //output >>> 191 | /* 192 | array(2) { 193 | [0]=> 194 | array(3) { 195 | ["jsonrpc"]=> 196 | string(3) "2.0" 197 | ["id"]=> 198 | int(110507766) 199 | ["result"]=> 200 | int(8) 201 | } 202 | [1]=> 203 | array(3) { 204 | ["jsonrpc"]=> 205 | string(3) "2.0" 206 | ["id"]=> 207 | int(1559316299) 208 | ["result"]=> 209 | int(30) 210 | } 211 | ... 212 | } 213 | */ 214 | ?> 215 | ``` 216 | **Custom ID** 217 | ```php 218 | call('http://localhost/server.php', 'addition', array(3,5),"custom_id_001"); 222 | $result = $client->execute(); 223 | var_dump($result); 224 | 225 | //output >>> 226 | /* 227 | array(1) { 228 | [0]=> 229 | array(3) { 230 | ["jsonrpc"]=> 231 | string(3) "2.0" 232 | ["id"]=> 233 | string(13) "custom_id_001" 234 | ["result"]=> 235 | int(8) 236 | } 237 | } 238 | */ 239 | ?> 240 | ``` 241 | 242 | **Authentication** 243 | ```php 244 | authentication("Username: username", "Password: password"); 249 | 250 | $client->call('http://localhost/server.php', 'addition1', array(3,5)); 251 | $result = $client->execute(); 252 | 253 | ?> 254 | ``` 255 | 256 | YAJL generate/parse 257 | ------------------- 258 | **Interface** 259 | - Jsonrpc_Yajl::generate(array $array) 260 | - Jsonrpc_Yajl::parse(string $json) 261 | 262 | **generate** 263 | ```php 264 | "value") 270 | ); 271 | 272 | var_dump(Jsonrpc_Yajl::generate($arr)); 273 | 274 | /* ==>output 275 | string(28) "[1,"string",{"key":"value"}]"; 276 | */ 277 | 278 | ?> 279 | ``` 280 | 281 | **parse** 282 | ```php 283 | output 290 | array(3) { 291 | [0]=> 292 | int(1) 293 | [1]=> 294 | string(6) "string" 295 | [2]=> 296 | array(1) { 297 | ["key"]=> 298 | string(5) "value" 299 | } 300 | } 301 | */ 302 | 303 | ?> 304 | ``` 305 | 306 | 307 | Error Info 308 | -------------- 309 | **jsonrpc 2.0 Error** 310 | ```javascript 311 | // Parse error 312 | {"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error"}} 313 | 314 | // Invalid Request 315 | {"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"Invalid Request"}} 316 | 317 | // Method not found 318 | {"jsonrpc":"2.0","id":null,"error":{"code":-32601,"message":"Method not found"}} 319 | 320 | // Invalid params 321 | {"jsonrpc":"2.0","id":null,"error":{"code":-32602,"message":"Invalid params"}} 322 | 323 | // 324 | ``` 325 | 326 | **HTTP Error** 327 | ```javascript 328 | // 400 329 | {"jsonrpc":"2.0","id":null,"error":{"code":-32400,"message":"Bad Request"}} 330 | // 401 331 | {"jsonrpc":"2.0","id":null,"error":{"code":-32401,"message":"Unauthorized"}} 332 | // 403 333 | {"jsonrpc":"2.0","id":null,"error":{"code":-32403,"message":"Forbidden"}} 334 | // 404 335 | {"jsonrpc":"2.0","id":null,"error":{"code":-32404,"message":"Not Found"}} 336 | 337 | // 500 338 | {"jsonrpc":"2.0","id":null,"error":{"code":-32500,"message":"Internal Server Error"}} 339 | // 502 340 | {"jsonrpc":"2.0","id":null,"error":{"code":-32502,"message":"Bad Gateway"}} 341 | ... 342 | 343 | // unknow 344 | {"jsonrpc":"2.0","id":null,"error":{"code":-32599,"message":"HTTP Unknow"}} 345 | ``` 346 | 347 | **Curl Error** 348 | ```javascript 349 | // 1 CURLE_UNSUPPORTED_PROTOCOL 350 | {"jsonrpc":"2.0","id":null,"error":{"code":-32001,"message":"Curl Unsupported Protocol"}} 351 | 352 | // 2 CURLE_FAILED_INIT 353 | {"jsonrpc":"2.0","id":null,"error":{"code":-32002,"message":"Curl Failed Init"}} 354 | 355 | // 3 CURLE_URL_MALFORMAT 356 | {"jsonrpc":"2.0","id":null,"error":{"code":-32003,"message":"Curl Url Malformat"}} 357 | 358 | // 4 359 | {"jsonrpc":"2.0","id":null,"error":{"code":-32004,"message":"Curl Not Built In"}} 360 | 361 | // 5 CURLE_COULDNT_RESOLVE_PROXY 362 | {"jsonrpc":"2.0","id":null,"error":{"code":-32005,"message":"Curl Couldnt Resolve Proxy"}} 363 | 364 | // 6 CURLE_COULDNT_RESOLVE_HOST 365 | {"jsonrpc":"2.0","id":null,"error":{"code":-32006,"message":"Curl Couldnt Resolve Host"}} 366 | 367 | // 7 CURLE_COULDNT_CONNECT 368 | {"jsonrpc":"2.0","id":null,"error":{"code":-32007,"message":"Curl Couldnt Connect"}} 369 | ... 370 | 371 | // CURL ERROR UNKNOW 372 | {"jsonrpc":"2.0","id":null,"error":{"code":-32099,"message":"Curl Error Unknow"}} 373 | ``` 374 | 375 | -------------------------------------------------------------------------------- /jsr_curl.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #include "jsr_curl.h" 22 | 23 | void * 24 | jsr_curl_global_new() 25 | { 26 | curl_global_init(JSR_CURL_GLOBAL_DEFAULT); 27 | } 28 | 29 | void * 30 | jsr_curl_global_destroy() 31 | { 32 | curl_global_cleanup(); 33 | } 34 | 35 | jsr_curl_t * 36 | jsr_curl_new() 37 | { 38 | jsr_curl_t *jsr_curl = (jsr_curl_t *)malloc(sizeof(jsr_curl_t)); 39 | if (!jsr_curl) 40 | return NULL; 41 | 42 | jsr_curl->curl_handle = curl_easy_init(); 43 | jsr_curl->multi_handle = curl_multi_init(); 44 | 45 | return jsr_curl; 46 | } 47 | 48 | void * 49 | jsr_curl_destroy(jsr_curl_t **self_p) 50 | { 51 | if (*self_p){ 52 | jsr_curl_t *self = *self_p; 53 | curl_multi_cleanup(self->multi_handle); 54 | curl_easy_cleanup(self->curl_handle); 55 | free(self); 56 | *self_p = NULL; 57 | } 58 | } 59 | 60 | jsr_curlm_t * 61 | jsr_curlm_new(void) 62 | { 63 | jsr_curlm_t *jsr_curlm = (jsr_curlm_t *)malloc(sizeof(jsr_curlm_t)); 64 | if (!jsr_curlm) 65 | return NULL; 66 | 67 | jsr_curlm->multi_handle = curl_multi_init(); 68 | jsr_curlm->list = jsr_list_new(); 69 | jsr_curlm->running_handles = 1; 70 | jsr_curlm->timeout = 1; 71 | 72 | return jsr_curlm; 73 | 74 | } 75 | 76 | void * 77 | jsr_curlm_destroy(jsr_curlm_t **self_p) 78 | { 79 | jsr_node_t *node; 80 | jsr_curl_item_t *item; 81 | 82 | if (*self_p){ 83 | jsr_curlm_t *self = *self_p; 84 | /*for (node = jsr_list_first(self->list) ; node != NULL; node = jsr_list_next(self->list)) 85 | { 86 | item = jsr_list_item(self->list); 87 | jsr_curl_item_destroy(&item); 88 | }*/ 89 | curl_multi_cleanup(self->multi_handle); 90 | jsr_list_free(&self->list); 91 | free(self); 92 | *self_p = NULL; 93 | } 94 | } 95 | 96 | int 97 | jsr_curlm_list_append(jsr_curlm_t *self, jsr_curl_item_t *item) 98 | { 99 | if (!self || !item) 100 | return -1; 101 | 102 | if (jsr_list_append(self->list, item)) 103 | return -1; 104 | 105 | return 0; 106 | } 107 | 108 | int 109 | jsr_curlm_list_push(jsr_curlm_t *self, jsr_curl_item_t *item) 110 | { 111 | if (!self || !item) 112 | return -1; 113 | 114 | if (jsr_list_push(self->list, item)) 115 | return -1; 116 | 117 | return 0; 118 | } 119 | 120 | jsr_curl_item_t * 121 | jsr_curlm_list_pop(jsr_curlm_t *self) 122 | { 123 | if (!self) 124 | return NULL; 125 | 126 | return jsr_list_pop(self->list); 127 | 128 | } 129 | 130 | jsr_curl_item_t * 131 | jsr_curl_item_new(char *url, size_t url_size, char *field, size_t field_size, int response_id, zval *payload_id) 132 | { 133 | jsr_curl_item_t *item = (jsr_curl_item_t *)malloc(sizeof(jsr_curl_item_t)); 134 | if (!item) 135 | return NULL; 136 | 137 | item->curl_handle = curl_easy_init(); 138 | 139 | memset(item->url, 0, 256); 140 | strcpy(item->url, url); 141 | 142 | memset(item->post_field, 0, 512); 143 | strcpy(item->post_field, field); 144 | 145 | item->post_field_size = field_size; 146 | 147 | item->timeout = 5; 148 | 149 | //item->fp = fopen("curl_data.txt", "ab+"); 150 | 151 | item->verbose = 0; 152 | 153 | item->slist = NULL; 154 | //item->slist = curl_slist_append(item->slist, "Content-Type: application/json; charset=utf-8"); 155 | item->slist = curl_slist_append(item->slist, "Accept: application/json"); 156 | 157 | item->response_id = response_id; 158 | 159 | if (Z_TYPE_P(payload_id) == IS_LONG){ 160 | item->payload_id.type = IS_LONG; 161 | item->payload_id.long_id = Z_LVAL_P(payload_id); 162 | }else if(Z_TYPE_P(payload_id) == IS_STRING){ 163 | item->payload_id.type = IS_STRING; 164 | memset(item->payload_id.char_id, 0, 64); 165 | strcpy(item->payload_id.char_id, Z_STRVAL_P(payload_id)); 166 | }else { 167 | item->payload_id.type = IS_NULL; 168 | } 169 | 170 | item->write_data = NULL; 171 | item->write_length = 0; 172 | 173 | return item; 174 | } 175 | 176 | void * 177 | jsr_curl_item_destroy(jsr_curl_item_t **self_p) 178 | { 179 | if (*self_p) 180 | { 181 | jsr_curl_item_t *self = *self_p; 182 | //if (self->curl_handle) 183 | //curl_easy_cleanup(self->curl_handle); 184 | //fclose(self->fp); 185 | free(self); 186 | *self_p = NULL; 187 | } 188 | } 189 | 190 | void * 191 | jsr_curl_item_setopt(jsr_curl_item_t *self) 192 | { 193 | curl_easy_setopt(self->curl_handle, CURLOPT_URL, self->url); 194 | curl_easy_setopt(self->curl_handle, CURLOPT_CONNECTTIMEOUT, self->timeout); 195 | curl_easy_setopt(self->curl_handle, CURLOPT_USERAGENT, "JSON-RPC PHP Client"); 196 | curl_easy_setopt(self->curl_handle, CURLOPT_HTTPHEADER, self->slist); 197 | curl_easy_setopt(self->curl_handle, CURLOPT_FOLLOWLOCATION, 0); 198 | curl_easy_setopt(self->curl_handle, CURLOPT_CUSTOMREQUEST, "POST"); 199 | curl_easy_setopt(self->curl_handle, CURLOPT_SSL_VERIFYPEER, 1); 200 | 201 | curl_easy_setopt(self->curl_handle, CURLOPT_NOPROGRESS, 1); 202 | 203 | curl_easy_setopt(self->curl_handle, CURLOPT_PRIVATE, self); 204 | 205 | //curl_easy_setopt(self->curl_handle, CURLOPT_POSTFIELDS, "{\"jsonrpc\":\"2.0\",\"method\":\"addition\",\"id\":1793433748,\"params\":[3,5]}"); 206 | curl_easy_setopt(self->curl_handle, CURLOPT_POSTFIELDS, self->post_field); 207 | curl_easy_setopt(self->curl_handle, CURLOPT_POSTFIELDSIZE, self->post_field_size); 208 | 209 | curl_easy_setopt(self->curl_handle, CURLOPT_WRITEFUNCTION, self->write_callback); 210 | curl_easy_setopt(self->curl_handle, CURLOPT_WRITEDATA, self); 211 | 212 | //curl_easy_setopt(self->curl_handle, CURLOPT_READFUNCTION, self->read_callback); 213 | //curl_easy_setopt(self->curl_handle, CURLOPT_READDATA, self); 214 | if (self->verbose){ 215 | curl_easy_setopt(self->curl_handle, CURLOPT_DEBUGFUNCTION, _jsr_curl_trace); 216 | curl_easy_setopt(self->curl_handle, CURLOPT_VERBOSE, self->verbose); 217 | } 218 | 219 | //curl_easy_setopt(self->curl_handle, CURLOPT_WRITEDATA, self->fp); 220 | 221 | } 222 | 223 | void * 224 | jsr_curlm_add_post(jsr_curlm_t *self) 225 | { 226 | jsr_curl_item_t *item; 227 | int still_running; 228 | 229 | size_t size; 230 | 231 | size = jsr_list_size(self->list); 232 | 233 | while (size > 0){ 234 | item = jsr_list_next(self->list); 235 | //php_printf("%d, %d\n", self->multi_handle, item->curl_handle); 236 | curl_multi_add_handle(self->multi_handle, item->curl_handle); 237 | size--; 238 | } 239 | 240 | self->list->cursor = NULL; 241 | 242 | //curl_multi_perform(self->multi_handle, &still_running); 243 | 244 | } 245 | 246 | static void 247 | _jsr_curl_dump(const char *text, 248 | FILE *stream, unsigned char *ptr, size_t size, 249 | bool nohex) 250 | { 251 | size_t i; 252 | size_t c; 253 | 254 | unsigned int width=0x10; 255 | 256 | if(nohex) 257 | /* without the hex output, we can fit more on screen */ 258 | width = 0x40; 259 | 260 | fprintf(stream, "%s, %10.10ld bytes (0x%8.8lx)\n", 261 | text, (long)size, (long)size); 262 | 263 | for(i=0; i=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.'); 284 | /* check again for 0D0A, to avoid an extra \n if it's at width */ 285 | if (nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) { 286 | i+=(c+3-width); 287 | break; 288 | } 289 | } 290 | fputc('\n', stream); /* newline */ 291 | } 292 | fflush(stream); 293 | } 294 | 295 | static int 296 | _jsr_curl_trace(CURL *handle, curl_infotype type, 297 | unsigned char *data, size_t size, 298 | void *userp) 299 | { 300 | const char *text; 301 | 302 | (void)userp; 303 | (void)handle; /* prevent compiler warning */ 304 | 305 | switch (type) { 306 | case CURLINFO_TEXT: 307 | fprintf(stderr, "== Info: %s", data); 308 | default: /* in case a new one is introduced to shock us */ 309 | return 0; 310 | 311 | case CURLINFO_HEADER_OUT: 312 | text = "=> Send header"; 313 | break; 314 | case CURLINFO_DATA_OUT: 315 | text = "=> Send data"; 316 | break; 317 | case CURLINFO_HEADER_IN: 318 | text = "<= Recv header"; 319 | break; 320 | case CURLINFO_DATA_IN: 321 | text = "<= Recv data"; 322 | break; 323 | } 324 | 325 | _jsr_curl_dump(text, stderr, data, size, TRUE); 326 | return 0; 327 | } 328 | 329 | /* 330 | * Local variables: 331 | * tab-width: 4 332 | * c-basic-offset: 4 333 | * End: 334 | * vim600: noet sw=4 ts=4 fdm=marker 335 | * vim<600: noet sw=4 ts=4 336 | */ -------------------------------------------------------------------------------- /yajl/api/yajl_parse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /** 18 | * \file yajl_parse.h 19 | * Interface to YAJL's JSON stream parsing facilities. 20 | */ 21 | 22 | #include "yajl_common.h" 23 | 24 | #ifndef __YAJL_PARSE_H__ 25 | #define __YAJL_PARSE_H__ 26 | 27 | #include 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | /** error codes returned from this interface */ 33 | typedef enum { 34 | /** no error was encountered */ 35 | yajl_status_ok, 36 | /** a client callback returned zero, stopping the parse */ 37 | yajl_status_client_canceled, 38 | /** An error occured during the parse. Call yajl_get_error for 39 | * more information about the encountered error */ 40 | yajl_status_error 41 | } yajl_status; 42 | 43 | /** attain a human readable, english, string for an error */ 44 | YAJL_API const char * yajl_status_to_string(yajl_status code); 45 | 46 | /** an opaque handle to a parser */ 47 | typedef struct yajl_handle_t * yajl_handle; 48 | 49 | /** yajl is an event driven parser. this means as json elements are 50 | * parsed, you are called back to do something with the data. The 51 | * functions in this table indicate the various events for which 52 | * you will be called back. Each callback accepts a "context" 53 | * pointer, this is a void * that is passed into the yajl_parse 54 | * function which the client code may use to pass around context. 55 | * 56 | * All callbacks return an integer. If non-zero, the parse will 57 | * continue. If zero, the parse will be canceled and 58 | * yajl_status_client_canceled will be returned from the parse. 59 | * 60 | * \attention { 61 | * A note about the handling of numbers: 62 | * 63 | * yajl will only convert numbers that can be represented in a 64 | * double or a 64 bit (long long) int. All other numbers will 65 | * be passed to the client in string form using the yajl_number 66 | * callback. Furthermore, if yajl_number is not NULL, it will 67 | * always be used to return numbers, that is yajl_integer and 68 | * yajl_double will be ignored. If yajl_number is NULL but one 69 | * of yajl_integer or yajl_double are defined, parsing of a 70 | * number larger than is representable in a double or 64 bit 71 | * integer will result in a parse error. 72 | * } 73 | */ 74 | typedef struct { 75 | int (* yajl_null)(void * ctx); 76 | int (* yajl_boolean)(void * ctx, int boolVal); 77 | int (* yajl_integer)(void * ctx, long long integerVal); 78 | int (* yajl_double)(void * ctx, double doubleVal); 79 | /** A callback which passes the string representation of the number 80 | * back to the client. Will be used for all numbers when present */ 81 | int (* yajl_number)(void * ctx, const char * numberVal, 82 | size_t numberLen); 83 | 84 | /** strings are returned as pointers into the JSON text when, 85 | * possible, as a result, they are _not_ null padded */ 86 | int (* yajl_string)(void * ctx, const unsigned char * stringVal, 87 | size_t stringLen); 88 | 89 | int (* yajl_start_map)(void * ctx); 90 | int (* yajl_map_key)(void * ctx, const unsigned char * key, 91 | size_t stringLen); 92 | int (* yajl_end_map)(void * ctx); 93 | 94 | int (* yajl_start_array)(void * ctx); 95 | int (* yajl_end_array)(void * ctx); 96 | } yajl_callbacks; 97 | 98 | /** allocate a parser handle 99 | * \param callbacks a yajl callbacks structure specifying the 100 | * functions to call when different JSON entities 101 | * are encountered in the input text. May be NULL, 102 | * which is only useful for validation. 103 | * \param afs memory allocation functions, may be NULL for to use 104 | * C runtime library routines (malloc and friends) 105 | * \param ctx a context pointer that will be passed to callbacks. 106 | */ 107 | YAJL_API yajl_handle yajl_alloc(const yajl_callbacks * callbacks, 108 | yajl_alloc_funcs * afs, 109 | void * ctx); 110 | 111 | 112 | /** configuration parameters for the parser, these may be passed to 113 | * yajl_config() along with option specific argument(s). In general, 114 | * all configuration parameters default to *off*. */ 115 | typedef enum { 116 | /** Ignore javascript style comments present in 117 | * JSON input. Non-standard, but rather fun 118 | * arguments: toggled off with integer zero, on otherwise. 119 | * 120 | * example: 121 | * yajl_config(h, yajl_allow_comments, 1); // turn comment support on 122 | */ 123 | yajl_allow_comments = 0x01, 124 | /** 125 | * When set the parser will verify that all strings in JSON input are 126 | * valid UTF8 and will emit a parse error if this is not so. When set, 127 | * this option makes parsing slightly more expensive (~7% depending 128 | * on processor and compiler in use) 129 | * 130 | * example: 131 | * yajl_config(h, yajl_dont_validate_strings, 1); // disable utf8 checking 132 | */ 133 | yajl_dont_validate_strings = 0x02, 134 | /** 135 | * By default, upon calls to yajl_complete_parse(), yajl will 136 | * ensure the entire input text was consumed and will raise an error 137 | * otherwise. Enabling this flag will cause yajl to disable this 138 | * check. This can be useful when parsing json out of a that contains more 139 | * than a single JSON document. 140 | */ 141 | yajl_allow_trailing_garbage = 0x04, 142 | /** 143 | * Allow multiple values to be parsed by a single handle. The 144 | * entire text must be valid JSON, and values can be seperated 145 | * by any kind of whitespace. This flag will change the 146 | * behavior of the parser, and cause it continue parsing after 147 | * a value is parsed, rather than transitioning into a 148 | * complete state. This option can be useful when parsing multiple 149 | * values from an input stream. 150 | */ 151 | yajl_allow_multiple_values = 0x08, 152 | /** 153 | * When yajl_complete_parse() is called the parser will 154 | * check that the top level value was completely consumed. I.E., 155 | * if called whilst in the middle of parsing a value 156 | * yajl will enter an error state (premature EOF). Setting this 157 | * flag suppresses that check and the corresponding error. 158 | */ 159 | yajl_allow_partial_values = 0x10 160 | } yajl_option; 161 | 162 | /** allow the modification of parser options subsequent to handle 163 | * allocation (via yajl_alloc) 164 | * \returns zero in case of errors, non-zero otherwise 165 | */ 166 | YAJL_API int yajl_config(yajl_handle h, yajl_option opt, ...); 167 | 168 | /** free a parser handle */ 169 | YAJL_API void yajl_free(yajl_handle handle); 170 | 171 | /** Parse some json! 172 | * \param hand - a handle to the json parser allocated with yajl_alloc 173 | * \param jsonText - a pointer to the UTF8 json text to be parsed 174 | * \param jsonTextLength - the length, in bytes, of input text 175 | */ 176 | YAJL_API yajl_status yajl_parse(yajl_handle hand, 177 | const unsigned char * jsonText, 178 | size_t jsonTextLength); 179 | 180 | /** Parse any remaining buffered json. 181 | * Since yajl is a stream-based parser, without an explicit end of 182 | * input, yajl sometimes can't decide if content at the end of the 183 | * stream is valid or not. For example, if "1" has been fed in, 184 | * yajl can't know whether another digit is next or some character 185 | * that would terminate the integer token. 186 | * 187 | * \param hand - a handle to the json parser allocated with yajl_alloc 188 | */ 189 | YAJL_API yajl_status yajl_complete_parse(yajl_handle hand); 190 | 191 | /** get an error string describing the state of the 192 | * parse. 193 | * 194 | * If verbose is non-zero, the message will include the JSON 195 | * text where the error occured, along with an arrow pointing to 196 | * the specific char. 197 | * 198 | * \returns A dynamically allocated string will be returned which should 199 | * be freed with yajl_free_error 200 | */ 201 | YAJL_API unsigned char * yajl_get_error(yajl_handle hand, int verbose, 202 | const unsigned char * jsonText, 203 | size_t jsonTextLength); 204 | 205 | /** 206 | * get the amount of data consumed from the last chunk passed to YAJL. 207 | * 208 | * In the case of a successful parse this can help you understand if 209 | * the entire buffer was consumed (which will allow you to handle 210 | * "junk at end of input"). 211 | * 212 | * In the event an error is encountered during parsing, this function 213 | * affords the client a way to get the offset into the most recent 214 | * chunk where the error occured. 0 will be returned if no error 215 | * was encountered. 216 | */ 217 | YAJL_API size_t yajl_get_bytes_consumed(yajl_handle hand); 218 | 219 | /** free an error returned from yajl_get_error */ 220 | YAJL_API void yajl_free_error(yajl_handle hand, unsigned char * str); 221 | 222 | #ifdef __cplusplus 223 | } 224 | #endif 225 | 226 | #endif 227 | -------------------------------------------------------------------------------- /yajl/yajl_gen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014, Lloyd Hilaiel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "api/yajl_gen.h" 18 | #include "yajl_buf.h" 19 | #include "yajl_encode.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | typedef enum { 28 | yajl_gen_start, 29 | yajl_gen_map_start, 30 | yajl_gen_map_key, 31 | yajl_gen_map_val, 32 | yajl_gen_array_start, 33 | yajl_gen_in_array, 34 | yajl_gen_complete, 35 | yajl_gen_error 36 | } yajl_gen_state; 37 | 38 | struct yajl_gen_t 39 | { 40 | unsigned int flags; 41 | unsigned int depth; 42 | const char * indentString; 43 | yajl_gen_state state[YAJL_MAX_DEPTH]; 44 | yajl_print_t print; 45 | void * ctx; /* yajl_buf */ 46 | /* memory allocation routines */ 47 | yajl_alloc_funcs alloc; 48 | }; 49 | 50 | int 51 | yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...) 52 | { 53 | int rv = 1; 54 | va_list ap; 55 | va_start(ap, opt); 56 | 57 | switch(opt) { 58 | case yajl_gen_beautify: 59 | case yajl_gen_validate_utf8: 60 | case yajl_gen_escape_solidus: 61 | if (va_arg(ap, int)) g->flags |= opt; 62 | else g->flags &= ~opt; 63 | break; 64 | case yajl_gen_indent_string: { 65 | const char *indent = va_arg(ap, const char *); 66 | g->indentString = indent; 67 | for (; *indent; indent++) { 68 | if (*indent != '\n' 69 | && *indent != '\v' 70 | && *indent != '\f' 71 | && *indent != '\t' 72 | && *indent != '\r' 73 | && *indent != ' ') 74 | { 75 | g->indentString = NULL; 76 | rv = 0; 77 | } 78 | } 79 | break; 80 | } 81 | case yajl_gen_print_callback: 82 | yajl_buf_free(g->ctx); 83 | g->print = va_arg(ap, const yajl_print_t); 84 | g->ctx = va_arg(ap, void *); 85 | break; 86 | default: 87 | rv = 0; 88 | } 89 | 90 | va_end(ap); 91 | 92 | return rv; 93 | } 94 | 95 | 96 | 97 | yajl_gen 98 | yajl_gen_alloc(const yajl_alloc_funcs * afs) 99 | { 100 | yajl_gen g = NULL; 101 | yajl_alloc_funcs afsBuffer; 102 | 103 | /* first order of business is to set up memory allocation routines */ 104 | if (afs != NULL) { 105 | if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) 106 | { 107 | return NULL; 108 | } 109 | } else { 110 | yajl_set_default_alloc_funcs(&afsBuffer); 111 | afs = &afsBuffer; 112 | } 113 | 114 | g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t)); 115 | if (!g) return NULL; 116 | 117 | memset((void *) g, 0, sizeof(struct yajl_gen_t)); 118 | /* copy in pointers to allocation routines */ 119 | memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); 120 | 121 | g->print = (yajl_print_t)&yajl_buf_append; 122 | g->ctx = yajl_buf_alloc(&(g->alloc)); 123 | g->indentString = " "; 124 | 125 | return g; 126 | } 127 | 128 | void 129 | yajl_gen_reset(yajl_gen g, const char * sep) 130 | { 131 | g->depth = 0; 132 | memset((void *) &(g->state), 0, sizeof(g->state)); 133 | if (sep != NULL) g->print(g->ctx, sep, strlen(sep)); 134 | } 135 | 136 | void 137 | yajl_gen_free(yajl_gen g) 138 | { 139 | if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_free((yajl_buf)g->ctx); 140 | YA_FREE(&(g->alloc), g); 141 | } 142 | 143 | #define INSERT_SEP \ 144 | if (g->state[g->depth] == yajl_gen_map_key || \ 145 | g->state[g->depth] == yajl_gen_in_array) { \ 146 | g->print(g->ctx, ",", 1); \ 147 | if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); \ 148 | } else if (g->state[g->depth] == yajl_gen_map_val) { \ 149 | g->print(g->ctx, ":", 1); \ 150 | if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, " ", 1); \ 151 | } 152 | 153 | #define INSERT_WHITESPACE \ 154 | if ((g->flags & yajl_gen_beautify)) { \ 155 | if (g->state[g->depth] != yajl_gen_map_val) { \ 156 | unsigned int _i; \ 157 | for (_i=0;_idepth;_i++) \ 158 | g->print(g->ctx, \ 159 | g->indentString, \ 160 | (unsigned int)strlen(g->indentString)); \ 161 | } \ 162 | } 163 | 164 | #define ENSURE_NOT_KEY \ 165 | if (g->state[g->depth] == yajl_gen_map_key || \ 166 | g->state[g->depth] == yajl_gen_map_start) { \ 167 | return yajl_gen_keys_must_be_strings; \ 168 | } \ 169 | 170 | /* check that we're not complete, or in error state. in a valid state 171 | * to be generating */ 172 | #define ENSURE_VALID_STATE \ 173 | if (g->state[g->depth] == yajl_gen_error) { \ 174 | return yajl_gen_in_error_state;\ 175 | } else if (g->state[g->depth] == yajl_gen_complete) { \ 176 | return yajl_gen_generation_complete; \ 177 | } 178 | 179 | #define INCREMENT_DEPTH \ 180 | if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded; 181 | 182 | #define DECREMENT_DEPTH \ 183 | if (--(g->depth) >= YAJL_MAX_DEPTH) return yajl_gen_generation_complete; 184 | 185 | #define APPENDED_ATOM \ 186 | switch (g->state[g->depth]) { \ 187 | case yajl_gen_start: \ 188 | g->state[g->depth] = yajl_gen_complete; \ 189 | break; \ 190 | case yajl_gen_map_start: \ 191 | case yajl_gen_map_key: \ 192 | g->state[g->depth] = yajl_gen_map_val; \ 193 | break; \ 194 | case yajl_gen_array_start: \ 195 | g->state[g->depth] = yajl_gen_in_array; \ 196 | break; \ 197 | case yajl_gen_map_val: \ 198 | g->state[g->depth] = yajl_gen_map_key; \ 199 | break; \ 200 | default: \ 201 | break; \ 202 | } \ 203 | 204 | #define FINAL_NEWLINE \ 205 | if ((g->flags & yajl_gen_beautify) && g->state[g->depth] == yajl_gen_complete) \ 206 | g->print(g->ctx, "\n", 1); 207 | 208 | yajl_gen_status 209 | yajl_gen_integer(yajl_gen g, long long int number) 210 | { 211 | char i[32]; 212 | ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; 213 | sprintf(i, "%lld", number); 214 | g->print(g->ctx, i, (unsigned int)strlen(i)); 215 | APPENDED_ATOM; 216 | FINAL_NEWLINE; 217 | return yajl_gen_status_ok; 218 | } 219 | 220 | #if defined(_WIN32) || defined(WIN32) 221 | #include 222 | #define isnan _isnan 223 | #define isinf !_finite 224 | #endif 225 | 226 | yajl_gen_status 227 | yajl_gen_double(yajl_gen g, double number) 228 | { 229 | char i[32]; 230 | ENSURE_VALID_STATE; ENSURE_NOT_KEY; 231 | if (isnan(number) || isinf(number)) return yajl_gen_invalid_number; 232 | INSERT_SEP; INSERT_WHITESPACE; 233 | sprintf(i, "%.20g", number); 234 | if (strspn(i, "0123456789-") == strlen(i)) { 235 | strcat(i, ".0"); 236 | } 237 | g->print(g->ctx, i, (unsigned int)strlen(i)); 238 | APPENDED_ATOM; 239 | FINAL_NEWLINE; 240 | return yajl_gen_status_ok; 241 | } 242 | 243 | yajl_gen_status 244 | yajl_gen_number(yajl_gen g, const char * s, size_t l) 245 | { 246 | ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; 247 | g->print(g->ctx, s, l); 248 | APPENDED_ATOM; 249 | FINAL_NEWLINE; 250 | return yajl_gen_status_ok; 251 | } 252 | 253 | yajl_gen_status 254 | yajl_gen_string(yajl_gen g, const unsigned char * str, 255 | size_t len) 256 | { 257 | // if validation is enabled, check that the string is valid utf8 258 | // XXX: This checking could be done a little faster, in the same pass as 259 | // the string encoding 260 | if (g->flags & yajl_gen_validate_utf8) { 261 | if (!yajl_string_validate_utf8(str, len)) { 262 | return yajl_gen_invalid_string; 263 | } 264 | } 265 | ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE; 266 | g->print(g->ctx, "\"", 1); 267 | yajl_string_encode(g->print, g->ctx, str, len, g->flags & yajl_gen_escape_solidus); 268 | g->print(g->ctx, "\"", 1); 269 | APPENDED_ATOM; 270 | FINAL_NEWLINE; 271 | return yajl_gen_status_ok; 272 | } 273 | 274 | yajl_gen_status 275 | yajl_gen_null(yajl_gen g) 276 | { 277 | ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; 278 | g->print(g->ctx, "null", strlen("null")); 279 | APPENDED_ATOM; 280 | FINAL_NEWLINE; 281 | return yajl_gen_status_ok; 282 | } 283 | 284 | yajl_gen_status 285 | yajl_gen_bool(yajl_gen g, int boolean) 286 | { 287 | const char * val = boolean ? "true" : "false"; 288 | 289 | ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; 290 | g->print(g->ctx, val, (unsigned int)strlen(val)); 291 | APPENDED_ATOM; 292 | FINAL_NEWLINE; 293 | return yajl_gen_status_ok; 294 | } 295 | 296 | yajl_gen_status 297 | yajl_gen_map_open(yajl_gen g) 298 | { 299 | ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; 300 | INCREMENT_DEPTH; 301 | 302 | g->state[g->depth] = yajl_gen_map_start; 303 | g->print(g->ctx, "{", 1); 304 | if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); 305 | FINAL_NEWLINE; 306 | return yajl_gen_status_ok; 307 | } 308 | 309 | yajl_gen_status 310 | yajl_gen_map_close(yajl_gen g) 311 | { 312 | ENSURE_VALID_STATE; 313 | DECREMENT_DEPTH; 314 | 315 | if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); 316 | APPENDED_ATOM; 317 | INSERT_WHITESPACE; 318 | g->print(g->ctx, "}", 1); 319 | FINAL_NEWLINE; 320 | return yajl_gen_status_ok; 321 | } 322 | 323 | yajl_gen_status 324 | yajl_gen_array_open(yajl_gen g) 325 | { 326 | ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; 327 | INCREMENT_DEPTH; 328 | g->state[g->depth] = yajl_gen_array_start; 329 | g->print(g->ctx, "[", 1); 330 | if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); 331 | FINAL_NEWLINE; 332 | return yajl_gen_status_ok; 333 | } 334 | 335 | yajl_gen_status 336 | yajl_gen_array_close(yajl_gen g) 337 | { 338 | ENSURE_VALID_STATE; 339 | DECREMENT_DEPTH; 340 | if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); 341 | APPENDED_ATOM; 342 | INSERT_WHITESPACE; 343 | g->print(g->ctx, "]", 1); 344 | FINAL_NEWLINE; 345 | return yajl_gen_status_ok; 346 | } 347 | 348 | yajl_gen_status 349 | yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf, 350 | size_t * len) 351 | { 352 | if (g->print != (yajl_print_t)&yajl_buf_append) return yajl_gen_no_buf; 353 | *buf = yajl_buf_data((yajl_buf)g->ctx); 354 | *len = yajl_buf_len((yajl_buf)g->ctx); 355 | return yajl_gen_status_ok; 356 | } 357 | 358 | void 359 | yajl_gen_clear(yajl_gen g) 360 | { 361 | if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_clear((yajl_buf)g->ctx); 362 | } 363 | -------------------------------------------------------------------------------- /yajl/yajl_tree.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010-2011 Florian Forster 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "api/yajl_tree.h" 24 | #include "api/yajl_parse.h" 25 | 26 | #include "yajl_parser.h" 27 | 28 | #if defined(_WIN32) || defined(WIN32) 29 | #define snprintf sprintf_s 30 | #endif 31 | 32 | #define STATUS_CONTINUE 1 33 | #define STATUS_ABORT 0 34 | 35 | struct stack_elem_s; 36 | typedef struct stack_elem_s stack_elem_t; 37 | struct stack_elem_s 38 | { 39 | char * key; 40 | yajl_val value; 41 | stack_elem_t *next; 42 | }; 43 | 44 | struct context_s 45 | { 46 | stack_elem_t *stack; 47 | yajl_val root; 48 | char *errbuf; 49 | size_t errbuf_size; 50 | }; 51 | typedef struct context_s context_t; 52 | 53 | #define RETURN_ERROR(ctx,retval,...) { \ 54 | if ((ctx)->errbuf != NULL) \ 55 | snprintf ((ctx)->errbuf, (ctx)->errbuf_size, __VA_ARGS__); \ 56 | return (retval); \ 57 | } 58 | 59 | static yajl_val value_alloc (yajl_type type) 60 | { 61 | yajl_val v; 62 | 63 | v = malloc (sizeof (*v)); 64 | if (v == NULL) return (NULL); 65 | memset (v, 0, sizeof (*v)); 66 | v->type = type; 67 | 68 | return (v); 69 | } 70 | 71 | static void yajl_object_free (yajl_val v) 72 | { 73 | size_t i; 74 | 75 | if (!YAJL_IS_OBJECT(v)) return; 76 | 77 | for (i = 0; i < v->u.object.len; i++) 78 | { 79 | free((char *) v->u.object.keys[i]); 80 | v->u.object.keys[i] = NULL; 81 | yajl_tree_free (v->u.object.values[i]); 82 | v->u.object.values[i] = NULL; 83 | } 84 | 85 | free((void*) v->u.object.keys); 86 | free(v->u.object.values); 87 | free(v); 88 | } 89 | 90 | static void yajl_array_free (yajl_val v) 91 | { 92 | size_t i; 93 | 94 | if (!YAJL_IS_ARRAY(v)) return; 95 | 96 | for (i = 0; i < v->u.array.len; i++) 97 | { 98 | yajl_tree_free (v->u.array.values[i]); 99 | v->u.array.values[i] = NULL; 100 | } 101 | 102 | free(v->u.array.values); 103 | free(v); 104 | } 105 | 106 | /* 107 | * Parsing nested objects and arrays is implemented using a stack. When a new 108 | * object or array starts (a curly or a square opening bracket is read), an 109 | * appropriate value is pushed on the stack. When the end of the object is 110 | * reached (an appropriate closing bracket has been read), the value is popped 111 | * off the stack and added to the enclosing object using "context_add_value". 112 | */ 113 | static int context_push(context_t *ctx, yajl_val v) 114 | { 115 | stack_elem_t *stack; 116 | 117 | stack = malloc (sizeof (*stack)); 118 | if (stack == NULL) 119 | RETURN_ERROR (ctx, ENOMEM, "Out of memory"); 120 | memset (stack, 0, sizeof (*stack)); 121 | 122 | assert ((ctx->stack == NULL) 123 | || YAJL_IS_OBJECT (v) 124 | || YAJL_IS_ARRAY (v)); 125 | 126 | stack->value = v; 127 | stack->next = ctx->stack; 128 | ctx->stack = stack; 129 | 130 | return (0); 131 | } 132 | 133 | static yajl_val context_pop(context_t *ctx) 134 | { 135 | stack_elem_t *stack; 136 | yajl_val v; 137 | 138 | if (ctx->stack == NULL) 139 | RETURN_ERROR (ctx, NULL, "context_pop: " 140 | "Bottom of stack reached prematurely"); 141 | 142 | stack = ctx->stack; 143 | ctx->stack = stack->next; 144 | 145 | v = stack->value; 146 | 147 | free (stack); 148 | 149 | return (v); 150 | } 151 | 152 | static int object_add_keyval(context_t *ctx, 153 | yajl_val obj, char *key, yajl_val value) 154 | { 155 | const char **tmpk; 156 | yajl_val *tmpv; 157 | 158 | /* We're checking for NULL in "context_add_value" or its callers. */ 159 | assert (ctx != NULL); 160 | assert (obj != NULL); 161 | assert (key != NULL); 162 | assert (value != NULL); 163 | 164 | /* We're assuring that "obj" is an object in "context_add_value". */ 165 | assert(YAJL_IS_OBJECT(obj)); 166 | 167 | tmpk = realloc((void *) obj->u.object.keys, sizeof(*(obj->u.object.keys)) * (obj->u.object.len + 1)); 168 | if (tmpk == NULL) 169 | RETURN_ERROR(ctx, ENOMEM, "Out of memory"); 170 | obj->u.object.keys = tmpk; 171 | 172 | tmpv = realloc(obj->u.object.values, sizeof (*obj->u.object.values) * (obj->u.object.len + 1)); 173 | if (tmpv == NULL) 174 | RETURN_ERROR(ctx, ENOMEM, "Out of memory"); 175 | obj->u.object.values = tmpv; 176 | 177 | obj->u.object.keys[obj->u.object.len] = key; 178 | obj->u.object.values[obj->u.object.len] = value; 179 | obj->u.object.len++; 180 | 181 | return (0); 182 | } 183 | 184 | static int array_add_value (context_t *ctx, 185 | yajl_val array, yajl_val value) 186 | { 187 | yajl_val *tmp; 188 | 189 | /* We're checking for NULL pointers in "context_add_value" or its 190 | * callers. */ 191 | assert (ctx != NULL); 192 | assert (array != NULL); 193 | assert (value != NULL); 194 | 195 | /* "context_add_value" will only call us with array values. */ 196 | assert(YAJL_IS_ARRAY(array)); 197 | 198 | tmp = realloc(array->u.array.values, 199 | sizeof(*(array->u.array.values)) * (array->u.array.len + 1)); 200 | if (tmp == NULL) 201 | RETURN_ERROR(ctx, ENOMEM, "Out of memory"); 202 | array->u.array.values = tmp; 203 | array->u.array.values[array->u.array.len] = value; 204 | array->u.array.len++; 205 | 206 | return 0; 207 | } 208 | 209 | /* 210 | * Add a value to the value on top of the stack or the "root" member in the 211 | * context if the end of the parsing process is reached. 212 | */ 213 | static int context_add_value (context_t *ctx, yajl_val v) 214 | { 215 | /* We're checking for NULL values in all the calling functions. */ 216 | assert (ctx != NULL); 217 | assert (v != NULL); 218 | 219 | /* 220 | * There are three valid states in which this function may be called: 221 | * - There is no value on the stack => This is the only value. This is the 222 | * last step done when parsing a document. We assign the value to the 223 | * "root" member and return. 224 | * - The value on the stack is an object. In this case store the key on the 225 | * stack or, if the key has already been read, add key and value to the 226 | * object. 227 | * - The value on the stack is an array. In this case simply add the value 228 | * and return. 229 | */ 230 | if (ctx->stack == NULL) 231 | { 232 | assert (ctx->root == NULL); 233 | ctx->root = v; 234 | return (0); 235 | } 236 | else if (YAJL_IS_OBJECT (ctx->stack->value)) 237 | { 238 | if (ctx->stack->key == NULL) 239 | { 240 | if (!YAJL_IS_STRING (v)) 241 | RETURN_ERROR (ctx, EINVAL, "context_add_value: " 242 | "Object key is not a string (%#04x)", 243 | v->type); 244 | 245 | ctx->stack->key = v->u.string; 246 | v->u.string = NULL; 247 | free(v); 248 | return (0); 249 | } 250 | else /* if (ctx->key != NULL) */ 251 | { 252 | char * key; 253 | 254 | key = ctx->stack->key; 255 | ctx->stack->key = NULL; 256 | return (object_add_keyval (ctx, ctx->stack->value, key, v)); 257 | } 258 | } 259 | else if (YAJL_IS_ARRAY (ctx->stack->value)) 260 | { 261 | return (array_add_value (ctx, ctx->stack->value, v)); 262 | } 263 | else 264 | { 265 | RETURN_ERROR (ctx, EINVAL, "context_add_value: Cannot add value to " 266 | "a value of type %#04x (not a composite type)", 267 | ctx->stack->value->type); 268 | } 269 | } 270 | 271 | static int handle_string (void *ctx, 272 | const unsigned char *string, size_t string_length) 273 | { 274 | yajl_val v; 275 | 276 | v = value_alloc (yajl_t_string); 277 | if (v == NULL) 278 | RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); 279 | 280 | v->u.string = malloc (string_length + 1); 281 | if (v->u.string == NULL) 282 | { 283 | free (v); 284 | RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); 285 | } 286 | memcpy(v->u.string, string, string_length); 287 | v->u.string[string_length] = 0; 288 | 289 | return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 290 | } 291 | 292 | static int handle_number (void *ctx, const char *string, size_t string_length) 293 | { 294 | yajl_val v; 295 | char *endptr; 296 | 297 | v = value_alloc(yajl_t_number); 298 | if (v == NULL) 299 | RETURN_ERROR((context_t *) ctx, STATUS_ABORT, "Out of memory"); 300 | 301 | v->u.number.r = malloc(string_length + 1); 302 | if (v->u.number.r == NULL) 303 | { 304 | free(v); 305 | RETURN_ERROR((context_t *) ctx, STATUS_ABORT, "Out of memory"); 306 | } 307 | memcpy(v->u.number.r, string, string_length); 308 | v->u.number.r[string_length] = 0; 309 | 310 | v->u.number.flags = 0; 311 | 312 | errno = 0; 313 | v->u.number.i = yajl_parse_integer((const unsigned char *) v->u.number.r, 314 | strlen(v->u.number.r)); 315 | if (errno == 0) 316 | v->u.number.flags |= YAJL_NUMBER_INT_VALID; 317 | 318 | endptr = NULL; 319 | errno = 0; 320 | v->u.number.d = strtod(v->u.number.r, &endptr); 321 | if ((errno == 0) && (endptr != NULL) && (*endptr == 0)) 322 | v->u.number.flags |= YAJL_NUMBER_DOUBLE_VALID; 323 | 324 | return ((context_add_value(ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 325 | } 326 | 327 | static int handle_start_map (void *ctx) 328 | { 329 | yajl_val v; 330 | 331 | v = value_alloc(yajl_t_object); 332 | if (v == NULL) 333 | RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); 334 | 335 | v->u.object.keys = NULL; 336 | v->u.object.values = NULL; 337 | v->u.object.len = 0; 338 | 339 | return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 340 | } 341 | 342 | static int handle_end_map (void *ctx) 343 | { 344 | yajl_val v; 345 | 346 | v = context_pop (ctx); 347 | if (v == NULL) 348 | return (STATUS_ABORT); 349 | 350 | return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 351 | } 352 | 353 | static int handle_start_array (void *ctx) 354 | { 355 | yajl_val v; 356 | 357 | v = value_alloc(yajl_t_array); 358 | if (v == NULL) 359 | RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); 360 | 361 | v->u.array.values = NULL; 362 | v->u.array.len = 0; 363 | 364 | return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 365 | } 366 | 367 | static int handle_end_array (void *ctx) 368 | { 369 | yajl_val v; 370 | 371 | v = context_pop (ctx); 372 | if (v == NULL) 373 | return (STATUS_ABORT); 374 | 375 | return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 376 | } 377 | 378 | static int handle_boolean (void *ctx, int boolean_value) 379 | { 380 | yajl_val v; 381 | 382 | v = value_alloc (boolean_value ? yajl_t_true : yajl_t_false); 383 | if (v == NULL) 384 | RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); 385 | 386 | return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 387 | } 388 | 389 | static int handle_null (void *ctx) 390 | { 391 | yajl_val v; 392 | 393 | v = value_alloc (yajl_t_null); 394 | if (v == NULL) 395 | RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory"); 396 | 397 | return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 398 | } 399 | 400 | /* 401 | * Public functions 402 | */ 403 | yajl_val yajl_tree_parse (const char *input, 404 | char *error_buffer, size_t error_buffer_size) 405 | { 406 | static const yajl_callbacks callbacks = 407 | { 408 | /* null = */ handle_null, 409 | /* boolean = */ handle_boolean, 410 | /* integer = */ NULL, 411 | /* double = */ NULL, 412 | /* number = */ handle_number, 413 | /* string = */ handle_string, 414 | /* start map = */ handle_start_map, 415 | /* map key = */ handle_string, 416 | /* end map = */ handle_end_map, 417 | /* start array = */ handle_start_array, 418 | /* end array = */ handle_end_array 419 | }; 420 | 421 | yajl_handle handle; 422 | yajl_status status; 423 | char * internal_err_str; 424 | context_t ctx = { NULL, NULL, NULL, 0 }; 425 | 426 | ctx.errbuf = error_buffer; 427 | ctx.errbuf_size = error_buffer_size; 428 | 429 | if (error_buffer != NULL) 430 | memset (error_buffer, 0, error_buffer_size); 431 | 432 | handle = yajl_alloc (&callbacks, NULL, &ctx); 433 | yajl_config(handle, yajl_allow_comments, 1); 434 | 435 | status = yajl_parse(handle, 436 | (unsigned char *) input, 437 | strlen (input)); 438 | status = yajl_complete_parse (handle); 439 | if (status != yajl_status_ok) { 440 | if (error_buffer != NULL && error_buffer_size > 0) { 441 | internal_err_str = (char *) yajl_get_error(handle, 1, 442 | (const unsigned char *) input, 443 | strlen(input)); 444 | snprintf(error_buffer, error_buffer_size, "%s", internal_err_str); 445 | YA_FREE(&(handle->alloc), internal_err_str); 446 | } 447 | yajl_free (handle); 448 | return NULL; 449 | } 450 | 451 | yajl_free (handle); 452 | return (ctx.root); 453 | } 454 | 455 | yajl_val yajl_tree_get(yajl_val n, const char ** path, yajl_type type) 456 | { 457 | if (!path) return NULL; 458 | while (n && *path) { 459 | size_t i; 460 | size_t len; 461 | 462 | if (n->type != yajl_t_object) return NULL; 463 | len = n->u.object.len; 464 | for (i = 0; i < len; i++) { 465 | if (!strcmp(*path, n->u.object.keys[i])) { 466 | n = n->u.object.values[i]; 467 | break; 468 | } 469 | } 470 | if (i == len) return NULL; 471 | path++; 472 | } 473 | if (n && type != yajl_t_any && type != n->type) n = NULL; 474 | return n; 475 | } 476 | 477 | void yajl_tree_free (yajl_val v) 478 | { 479 | if (v == NULL) return; 480 | 481 | if (YAJL_IS_STRING(v)) 482 | { 483 | free(v->u.string); 484 | free(v); 485 | } 486 | else if (YAJL_IS_NUMBER(v)) 487 | { 488 | free(v->u.number.r); 489 | free(v); 490 | } 491 | else if (YAJL_GET_OBJECT(v)) 492 | { 493 | yajl_object_free(v); 494 | } 495 | else if (YAJL_GET_ARRAY(v)) 496 | { 497 | yajl_array_free(v); 498 | } 499 | else /* if (yajl_t_true or yajl_t_false or yajl_t_null) */ 500 | { 501 | free(v); 502 | } 503 | } 504 | -------------------------------------------------------------------------------- /jsr_yajl.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: rryqszq4 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | #include "jsr_yajl.h" 21 | 22 | static int le_yajl; 23 | 24 | ZEND_BEGIN_ARG_INFO_EX(arginfo_jsonrpc_yajl_construct, 0, 0, 0) 25 | ZEND_END_ARG_INFO() 26 | ZEND_BEGIN_ARG_INFO_EX(arginfo_jsonrpc_yajl_generate, 0, 0, 1) 27 | ZEND_ARG_INFO(0, value) 28 | ZEND_END_ARG_INFO() 29 | ZEND_BEGIN_ARG_INFO_EX(arginfo_jsonrpc_yajl_parse, 0, 0, 1) 30 | ZEND_ARG_INFO(0, str) 31 | ZEND_END_ARG_INFO() 32 | 33 | 34 | static int json_determine_array_type(zval **val TSRMLS_DC) /* {{{ */ 35 | { 36 | int i; 37 | HashTable *myht = HASH_OF(*val); 38 | 39 | i = myht ? zend_hash_num_elements(myht) : 0; 40 | if (i > 0) { 41 | char *key; 42 | ulong index, idx; 43 | uint key_len; 44 | HashPosition pos; 45 | 46 | zend_hash_internal_pointer_reset_ex(myht, &pos); 47 | idx = 0; 48 | for (;; zend_hash_move_forward_ex(myht, &pos)) { 49 | i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos); 50 | if (i == HASH_KEY_NON_EXISTANT) { 51 | break; 52 | } 53 | 54 | if (i == HASH_KEY_IS_STRING) { 55 | return 1; 56 | } else { 57 | if (index != idx) { 58 | return 1; 59 | } 60 | } 61 | idx++; 62 | } 63 | } 64 | 65 | return PHP_JSON_OUTPUT_ARRAY; 66 | } 67 | /* }}} */ 68 | 69 | static void php_yajl_generate_array(yajl_gen gen, zval *val TSRMLS_DC) 70 | { 71 | HashTable *hash_table; 72 | int i, r; 73 | 74 | if (Z_TYPE_P(val) == IS_ARRAY) 75 | { 76 | hash_table = HASH_OF(val); 77 | r = json_determine_array_type(&val TSRMLS_CC); 78 | if (r == PHP_JSON_OUTPUT_ARRAY){ 79 | yajl_gen_array_open(gen); 80 | }else { 81 | yajl_gen_map_open(gen); 82 | } 83 | }else { 84 | hash_table = Z_OBJPROP_P(val); 85 | r = PHP_JSON_OUTPUT_OBJECT; 86 | yajl_gen_map_open(gen); 87 | } 88 | 89 | if (hash_table && hash_table->nApplyCount > 1) { 90 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); 91 | yajl_gen_null(gen); 92 | return; 93 | } 94 | 95 | i = hash_table ? zend_hash_num_elements(hash_table) : 0; 96 | 97 | if (i > 0) 98 | { 99 | HashPosition pos; 100 | char *key; 101 | ulong index; 102 | uint key_len; 103 | zval **data; 104 | HashTable *tmp_ht; 105 | char key_index[10]; 106 | 107 | zend_hash_internal_pointer_reset_ex(hash_table, &pos); 108 | for (;; zend_hash_move_forward_ex(hash_table, &pos)) 109 | { 110 | i = zend_hash_get_current_key_ex(hash_table, &key, &key_len, &index, 0, &pos); 111 | if (i == HASH_KEY_NON_EXISTANT) 112 | break; 113 | 114 | if (zend_hash_get_current_data_ex(hash_table, (void **)&data, &pos) == SUCCESS) 115 | { 116 | tmp_ht = HASH_OF(*data); 117 | if (tmp_ht){ 118 | tmp_ht->nApplyCount++; 119 | } 120 | 121 | if (r == PHP_JSON_OUTPUT_ARRAY){ 122 | php_yajl_generate(gen, *data TSRMLS_CC); 123 | }else if (r == PHP_JSON_OUTPUT_OBJECT){ 124 | if (i == HASH_KEY_IS_STRING){ 125 | yajl_gen_string(gen, key, key_len-1); 126 | }else { 127 | sprintf(key_index, "%d", index); 128 | yajl_gen_string(gen, key_index, strlen(key_index)); 129 | } 130 | php_yajl_generate(gen, *data TSRMLS_CC); 131 | } 132 | 133 | if (tmp_ht) { 134 | tmp_ht->nApplyCount--; 135 | } 136 | } 137 | } 138 | } 139 | 140 | if (r == PHP_JSON_OUTPUT_ARRAY){ 141 | yajl_gen_array_close(gen); 142 | }else { 143 | yajl_gen_map_close(gen); 144 | } 145 | 146 | } 147 | 148 | void php_yajl_generate(yajl_gen gen, zval *val TSRMLS_DC) 149 | { 150 | switch(Z_TYPE_P(val)) 151 | { 152 | case IS_NULL: 153 | yajl_gen_null(gen); 154 | break; 155 | 156 | case IS_BOOL: 157 | if (Z_BVAL_P(val)){ 158 | yajl_gen_bool(gen, 1); 159 | }else { 160 | yajl_gen_bool(gen, 0); 161 | } 162 | break; 163 | 164 | case IS_LONG: 165 | yajl_gen_integer(gen, Z_LVAL_P(val)); 166 | break; 167 | 168 | case IS_DOUBLE: 169 | yajl_gen_double(gen, Z_DVAL_P(val)); 170 | break; 171 | 172 | case IS_STRING: 173 | yajl_gen_string(gen, Z_STRVAL_P(val), Z_STRLEN_P(val)); 174 | break; 175 | 176 | case IS_ARRAY: 177 | case IS_OBJECT: 178 | php_yajl_generate_array(gen, val TSRMLS_CC); 179 | break; 180 | 181 | default: 182 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "type is unsupported, generate as null"); 183 | yajl_gen_null(gen); 184 | break; 185 | } 186 | 187 | return ; 188 | } 189 | 190 | static int object_add_keyval(context_t *ctx, 191 | zval *obj, zval *key, zval *value) 192 | { 193 | 194 | /* We're checking for NULL in "context_add_value" or its callers. */ 195 | assert (ctx != NULL); 196 | assert (obj != NULL); 197 | assert (key != NULL); 198 | assert (value != NULL); 199 | 200 | /* We're assuring that "obj" is an object in "context_add_value". */ 201 | assert(Z_TYPE_P(obj) == IS_OBJECT); 202 | 203 | add_assoc_zval(obj, Z_STRVAL_P(key), value); 204 | 205 | return (0); 206 | } 207 | 208 | static int array_add_value (context_t *ctx, 209 | zval *array, zval *value) 210 | { 211 | 212 | /* We're checking for NULL pointers in "context_add_value" or its 213 | * callers. */ 214 | assert (ctx != NULL); 215 | assert (array != NULL); 216 | assert (value != NULL); 217 | 218 | /* "context_add_value" will only call us with array values. */ 219 | assert(YAJL_IS_ARRAY(array)); 220 | 221 | add_next_index_zval(array, value); 222 | 223 | return 0; 224 | } 225 | 226 | static int context_push(context_t *ctx, zval *v, int type) 227 | { 228 | stack_elem_t *stack; 229 | 230 | stack = emalloc (sizeof (*stack)); 231 | if (stack == NULL) 232 | RETURN_ERROR (ctx, ENOMEM, "Out of memory"); 233 | memset (stack, 0, sizeof (*stack)); 234 | 235 | assert ((ctx->stack == NULL) 236 | || Z_TYPE_P(v) == IS_OBJECT 237 | || Z_TYPE_P(v) == IS_ARRAY); 238 | 239 | stack->type = type; 240 | stack->key = NULL; 241 | stack->value = v; 242 | stack->next = ctx->stack; 243 | ctx->stack = stack; 244 | 245 | return (0); 246 | } 247 | 248 | static zval* context_pop(context_t *ctx) 249 | { 250 | stack_elem_t *stack; 251 | zval *v; 252 | 253 | if (ctx->stack == NULL) 254 | RETURN_ERROR (ctx, NULL, "context_pop: " 255 | "Bottom of stack reached prematurely"); 256 | 257 | stack = ctx->stack; 258 | ctx->stack = stack->next; 259 | 260 | v = stack->value; 261 | 262 | efree (stack); 263 | 264 | return (v); 265 | } 266 | 267 | static int context_add_value (context_t *ctx, zval *v) 268 | { 269 | assert (ctx != NULL); 270 | assert (v != NULL); 271 | 272 | if (ctx->stack == NULL) 273 | { 274 | assert (ctx->root == NULL); 275 | ctx->root = v; 276 | return (0); 277 | } 278 | else if (Z_TYPE_P(ctx->stack->value) == IS_ARRAY) 279 | { 280 | if (ctx->stack->type == 0){ 281 | if (ctx->stack->key == NULL) 282 | { 283 | if (Z_TYPE_P(v) != IS_STRING) 284 | RETURN_ERROR (ctx, EINVAL, "context_add_value: " 285 | "Object key is not a string (%#04x)", 286 | Z_TYPE_P(v)); 287 | 288 | ctx->stack->key = v; 289 | 290 | return (0); 291 | } 292 | else /* if (ctx->key != NULL) */ 293 | { 294 | zval * key; 295 | 296 | key = ctx->stack->key; 297 | ctx->stack->key = NULL; 298 | return (object_add_keyval (ctx, ctx->stack->value, key, v)); 299 | } 300 | }else if (ctx->stack->type == 1){ 301 | return (array_add_value (ctx, ctx->stack->value, v)); 302 | } 303 | } 304 | /*else if (Z_TYPE_P(ctx->stack->value) == IS_ARRAY) 305 | { 306 | 307 | return (array_add_value (ctx, ctx->stack->value, v)); 308 | }*/ 309 | else 310 | { 311 | RETURN_ERROR (ctx, EINVAL, "context_add_value: Cannot add value to " 312 | "a value of type %#04x (not a composite type)", 313 | Z_TYPE_P(ctx->stack->value)); 314 | } 315 | } 316 | 317 | static int handle_string (void *ctx, 318 | const unsigned char *string, size_t string_length) 319 | { 320 | zval *v; 321 | MAKE_STD_ZVAL(v); 322 | 323 | ZVAL_STRINGL(v, string, string_length, 1); 324 | 325 | return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 326 | } 327 | 328 | static int handle_number (void *ctx, const char *string, size_t string_length) 329 | { 330 | zval *v; 331 | MAKE_STD_ZVAL(v); 332 | 333 | ZVAL_STRINGL(v, string, string_length, 1); 334 | 335 | return ((context_add_value(ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 336 | } 337 | 338 | static int handle_integer(void *ctx, long long int value) 339 | { 340 | zval *v; 341 | MAKE_STD_ZVAL(v); 342 | 343 | ZVAL_LONG(v, value); 344 | 345 | return ((context_add_value(ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 346 | } 347 | 348 | static int handle_double(void *ctx, double value) 349 | { 350 | zval *v; 351 | MAKE_STD_ZVAL(v); 352 | 353 | ZVAL_DOUBLE(v, value); 354 | 355 | return ((context_add_value(ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 356 | } 357 | 358 | static int handle_start_map (void *ctx) 359 | { 360 | int type = 0; 361 | zval *v; 362 | MAKE_STD_ZVAL(v); 363 | 364 | array_init(v); 365 | 366 | return ((context_push (ctx, v, type) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 367 | } 368 | 369 | static int handle_end_map (void *ctx) 370 | { 371 | zval *v; 372 | 373 | v = context_pop (ctx); 374 | if (v == NULL) 375 | return (STATUS_ABORT); 376 | 377 | return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 378 | } 379 | 380 | static int handle_start_array (void *ctx) 381 | { 382 | int type = 1; 383 | zval *v; 384 | MAKE_STD_ZVAL(v); 385 | 386 | array_init(v); 387 | 388 | return ((context_push (ctx, v, type) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 389 | } 390 | 391 | static int handle_end_array (void *ctx) 392 | { 393 | zval *v; 394 | 395 | v = context_pop (ctx); 396 | if (v == NULL) 397 | return (STATUS_ABORT); 398 | 399 | return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 400 | } 401 | 402 | static int handle_boolean (void *ctx, int boolean_value) 403 | { 404 | zval *v; 405 | MAKE_STD_ZVAL(v); 406 | 407 | ZVAL_BOOL(v, boolean_value); 408 | 409 | return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 410 | } 411 | 412 | static int handle_null (void *ctx) 413 | { 414 | zval *v; 415 | MAKE_STD_ZVAL(v); 416 | 417 | ZVAL_NULL(v); 418 | 419 | return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT); 420 | } 421 | 422 | 423 | zval* yajl_zval_parse (const char *input, 424 | char *error_buffer, size_t error_buffer_size) 425 | { 426 | static const yajl_callbacks callbacks = 427 | { 428 | /* null = */ handle_null, 429 | /* boolean = */ handle_boolean, 430 | /* integer = */ handle_integer, 431 | /* double = */ handle_double, 432 | /* number = */ NULL,//handle_number, 433 | /* string = */ handle_string, 434 | /* start map = */ handle_start_map, 435 | /* map key = */ handle_string, 436 | /* end map = */ handle_end_map, 437 | /* start array = */ handle_start_array, 438 | /* end array = */ handle_end_array 439 | }; 440 | 441 | yajl_handle handle; 442 | yajl_status status; 443 | char * internal_err_str; 444 | context_t ctx = { NULL, NULL, NULL, 0}; 445 | 446 | ctx.errbuf = error_buffer; 447 | ctx.errbuf_size = error_buffer_size; 448 | 449 | if (error_buffer != NULL) 450 | memset (error_buffer, 0, error_buffer_size); 451 | 452 | handle = yajl_alloc (&callbacks, NULL, &ctx); 453 | yajl_config(handle, yajl_allow_comments, 1); 454 | 455 | status = yajl_parse(handle, 456 | (unsigned char *) input, 457 | strlen (input)); 458 | status = yajl_complete_parse (handle); 459 | if (status != yajl_status_ok) { 460 | if (error_buffer != NULL && error_buffer_size > 0) { 461 | internal_err_str = (char *) yajl_get_error(handle, 1, 462 | (const unsigned char *) input, 463 | strlen(input)); 464 | snprintf(error_buffer, error_buffer_size, "%s", internal_err_str); 465 | //YA_FREE(&(handle->alloc), internal_err_str); 466 | } 467 | yajl_free (handle); 468 | return NULL; 469 | } 470 | 471 | yajl_free (handle); 472 | return (ctx.root); 473 | } 474 | 475 | void yajl_zval_free (zval *v) 476 | { 477 | if (v == NULL) return; 478 | 479 | zval_ptr_dtor(&v); 480 | } 481 | 482 | static void php_yajl_rsrc_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) 483 | { 484 | php_yajl_t *yajl = (php_yajl_t *)rsrc->ptr; 485 | 486 | if (yajl->gen){ 487 | //yajl_gen_clear(yajl->gen); 488 | yajl_gen_free(yajl->gen); 489 | } 490 | 491 | if (yajl->ctx){ 492 | efree(yajl->ctx); 493 | } 494 | 495 | if (yajl->handle){ 496 | yajl_free(yajl->handle); 497 | } 498 | 499 | if (yajl){ 500 | efree(yajl); 501 | } 502 | } 503 | 504 | static zval* php_yajl_new() 505 | { 506 | TSRMLS_FETCH(); 507 | 508 | php_yajl_t *yajl; 509 | zval *return_value = NULL; 510 | char errbuf[1024]; 511 | 512 | static const yajl_callbacks callbacks = 513 | { 514 | /* null = */ handle_null, 515 | /* boolean = */ handle_boolean, 516 | /* integer = */ handle_integer, 517 | /* double = */ handle_double, 518 | /* number = */ NULL,//handle_number, 519 | /* string = */ handle_string, 520 | /* start map = */ handle_start_map, 521 | /* map key = */ handle_string, 522 | /* end map = */ handle_end_map, 523 | /* start array = */ handle_start_array, 524 | /* end array = */ handle_end_array 525 | }; 526 | 527 | yajl = emalloc(sizeof(php_yajl_t)); 528 | 529 | yajl->gen = yajl_gen_alloc(NULL); 530 | yajl_gen_config(yajl->gen, yajl_gen_beautify, 1); 531 | yajl_gen_config(yajl->gen, yajl_gen_validate_utf8, 1); 532 | yajl_gen_config(yajl->gen, yajl_gen_escape_solidus, 1); 533 | 534 | 535 | yajl->ctx = ecalloc(1, sizeof(context_t *)); 536 | 537 | yajl->handle = yajl_alloc (&callbacks, NULL, yajl->ctx); 538 | yajl_config(yajl->handle, yajl_allow_comments, 1); 539 | 540 | MAKE_STD_ZVAL(return_value); 541 | ZEND_REGISTER_RESOURCE(return_value, yajl, le_yajl); 542 | 543 | return return_value; 544 | } 545 | 546 | PHP_METHOD(jsonrpc_yajl, generate) 547 | { 548 | zval *instance; 549 | php_yajl_t *yajl; 550 | 551 | zval *param; 552 | const unsigned char * buf; 553 | size_t len; 554 | 555 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", ¶m) == FAILURE) 556 | { 557 | return ; 558 | } 559 | 560 | instance = zend_read_static_property(php_jsonrpc_yajl_class_entry, "instance", sizeof("instance")-1 , 1 TSRMLS_CC); 561 | if (Z_TYPE_P(instance) == IS_RESOURCE){ 562 | 563 | }else { 564 | instance = php_yajl_new(); 565 | zend_update_static_property(php_jsonrpc_yajl_class_entry, "instance", sizeof("instance")-1, instance TSRMLS_CC); 566 | } 567 | 568 | ZEND_FETCH_RESOURCE(yajl, php_yajl_t *, &instance, -1, "php yajl", le_yajl); 569 | 570 | yajl_gen_reset(yajl->gen, ""); 571 | 572 | php_yajl_generate(yajl->gen, param TSRMLS_CC); 573 | 574 | yajl_gen_get_buf(yajl->gen, &buf, &len); 575 | 576 | ZVAL_STRINGL(return_value, buf, len, 1); 577 | 578 | yajl_gen_clear(yajl->gen); 579 | 580 | return; 581 | 582 | } 583 | 584 | PHP_METHOD(jsonrpc_yajl, parse) 585 | { 586 | zval *instance; 587 | php_yajl_t *yajl; 588 | 589 | char *str; 590 | int str_len; 591 | 592 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) 593 | { 594 | return ; 595 | } 596 | 597 | instance = zend_read_static_property(php_jsonrpc_yajl_class_entry, "instance", sizeof("instance")-1 , 1 TSRMLS_CC); 598 | if (Z_TYPE_P(instance) == IS_RESOURCE){ 599 | 600 | }else { 601 | instance = php_yajl_new(); 602 | zend_update_static_property(php_jsonrpc_yajl_class_entry, "instance", sizeof("instance")-1, instance TSRMLS_CC); 603 | } 604 | 605 | ZEND_FETCH_RESOURCE(yajl, php_yajl_t *, &instance, -1, "php yajl", le_yajl); 606 | 607 | yajl->status = yajl_parse(yajl->handle, 608 | (unsigned char *) str, 609 | (size_t) str_len); 610 | 611 | yajl->status = yajl_complete_parse (yajl->handle); 612 | 613 | if (yajl->status != yajl_status_ok) { 614 | return ; 615 | } 616 | 617 | RETVAL_ZVAL(yajl->ctx->root, 1, 0); 618 | 619 | yajl_bs_free(yajl->handle->stateStack); 620 | yajl_bs_init(yajl->handle->stateStack, &(yajl->handle->alloc)); 621 | yajl_bs_push(yajl->handle->stateStack, 0); 622 | //yajl_bs_set((yajl->handle)->stateStack, 0); 623 | 624 | zval_ptr_dtor(&yajl->ctx->root); 625 | return ; 626 | 627 | } 628 | 629 | static const zend_function_entry php_yajl_class_functions[] = { 630 | //PHP_ME(yajl, __construct, arginfo_php_yajl_construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) 631 | PHP_ME(jsonrpc_yajl, generate, arginfo_jsonrpc_yajl_generate, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) 632 | PHP_ME(jsonrpc_yajl, parse, arginfo_jsonrpc_yajl_parse, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) 633 | {NULL, NULL, NULL} 634 | }; 635 | 636 | void 637 | jsonrpc_yajl_init(int module_number TSRMLS_DC) 638 | { 639 | le_yajl = zend_register_list_destructors_ex(php_yajl_rsrc_dtor, NULL, "php jsonrpc yajl", module_number); 640 | 641 | zend_class_entry yajl_class_entry; 642 | INIT_CLASS_ENTRY(yajl_class_entry, "Jsonrpc_Yajl", php_yajl_class_functions); 643 | php_jsonrpc_yajl_class_entry = zend_register_internal_class(&yajl_class_entry TSRMLS_CC); 644 | 645 | zend_declare_property_null(php_jsonrpc_yajl_class_entry, "instance", sizeof("instance")-1, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC TSRMLS_CC); 646 | } 647 | 648 | 649 | /* 650 | * Local variables: 651 | * tab-width: 4 652 | * c-basic-offset: 4 653 | * End: 654 | * vim600: noet sw=4 ts=4 fdm=marker 655 | * vim<600: noet sw=4 ts=4 656 | */ 657 | --------------------------------------------------------------------------------