JSON API - Version 1.1.2

Version Description

Download this release

Release Info

Developer dphiffer
Plugin Icon wp plugin JSON API
Version 1.1.2
Comparing to
See all releases

Code changes from version 1.1.1 to 1.1.2

Files changed (48) hide show
  1. controllers/posts.php +12 -12
  2. models/comment.php +2 -2
  3. singletons/api.php +2 -2
  4. singletons/query.php +1 -1
  5. singletons/response.php +5 -5
  6. trunk/controllers/core.php +339 -0
  7. trunk/controllers/posts.php +85 -0
  8. trunk/controllers/respond.php +27 -0
  9. trunk/controllers/widgets.php +108 -0
  10. trunk/json-api.php +83 -0
  11. trunk/library/JSON.php +933 -0
  12. trunk/models/attachment.php +64 -0
  13. trunk/models/author.php +62 -0
  14. trunk/models/category.php +29 -0
  15. trunk/models/comment.php +80 -0
  16. trunk/models/post.php +319 -0
  17. trunk/models/tag.php +26 -0
  18. trunk/readme.txt +1241 -0
  19. trunk/screenshot-1.png +0 -0
  20. trunk/singletons/api.php +394 -0
  21. trunk/singletons/introspector.php +345 -0
  22. trunk/singletons/query.php +194 -0
  23. trunk/singletons/response.php +205 -0
  24. trunk/tests/core.get_author_index-01.phpt +21 -0
  25. trunk/tests/core.get_author_posts-01.phpt +22 -0
  26. trunk/tests/core.get_author_posts-02.phpt +22 -0
  27. trunk/tests/core.get_author_posts-03.phpt +22 -0
  28. trunk/tests/core.get_category_index-01.phpt +21 -0
  29. trunk/tests/core.get_category_posts-01.phpt +21 -0
  30. trunk/tests/core.get_date_index-01.phpt +80 -0
  31. trunk/tests/core.get_date_posts-01.phpt +21 -0
  32. trunk/tests/core.get_date_posts-02.phpt +21 -0
  33. trunk/tests/core.get_date_posts-03.phpt +21 -0
  34. trunk/tests/core.get_page-01.phpt +19 -0
  35. trunk/tests/core.get_page-02.phpt +25 -0
  36. trunk/tests/core.get_post-01.phpt +18 -0
  37. trunk/tests/core.get_posts-01.phpt +21 -0
  38. trunk/tests/core.get_posts-02.phpt +24 -0
  39. trunk/tests/core.get_posts-03.phpt +23 -0
  40. trunk/tests/core.get_recent_posts-01.phpt +21 -0
  41. trunk/tests/core.get_recent_posts-02.phpt +21 -0
  42. trunk/tests/core.get_recent_posts-03.phpt +21 -0
  43. trunk/tests/core.get_search_posts-01.phpt +21 -0
  44. trunk/tests/core.get_tag_index-01.phpt +803 -0
  45. trunk/tests/core.get_tag_posts-01.phpt +21 -0
  46. trunk/tests/core.info-01.phpt +29 -0
  47. trunk/tests/core.info-02.phpt +57 -0
  48. trunk/tests/query-01.phpt +21 -0
controllers/posts.php CHANGED
@@ -9,20 +9,20 @@ class JSON_API_Posts_Controller {
9
  public function create_post() {
10
  global $json_api;
11
  if (!current_user_can('edit_posts')) {
12
- $json_api->error("You need to login with a user that has 'edit_posts' capacity.");
13
  }
14
  if (!$json_api->query->nonce) {
15
- $json_api->error("You must include a 'nonce' value to create posts. Use the `get_nonce` Core API method.");
16
  }
17
  $nonce_id = $json_api->get_nonce_id('posts', 'create_post');
18
  if (!wp_verify_nonce($json_api->query->nonce, $nonce_id)) {
19
- $json_api->error("Your 'nonce' value was incorrect. Use the 'get_nonce' API method.");
20
  }
21
  nocache_headers();
22
  $post = new JSON_API_Post();
23
  $id = $post->create($_REQUEST);
24
  if (empty($id)) {
25
- $json_api->error("Could not create post.");
26
  }
27
  return array(
28
  'post' => $post
@@ -36,14 +36,14 @@ class JSON_API_Posts_Controller {
36
  $json_api->error("Post not found.");
37
  }
38
  if (!current_user_can('edit_post', $post->ID)) {
39
- $json_api->error("You need to login with a user that has the 'edit_post' capacity for that post.");
40
  }
41
  if (!$json_api->query->nonce) {
42
- $json_api->error("You must include a 'nonce' value to update posts. Use the `get_nonce` Core API method.");
43
  }
44
  $nonce_id = $json_api->get_nonce_id('posts', 'update_post');
45
  if (!wp_verify_nonce($json_api->query->nonce, $nonce_id)) {
46
- $json_api->error("Your 'nonce' value was incorrect. Use the 'get_nonce' API method.");
47
  }
48
  nocache_headers();
49
  $post = new JSON_API_Post($post);
@@ -60,20 +60,20 @@ class JSON_API_Posts_Controller {
60
  $json_api->error("Post not found.");
61
  }
62
  if (!current_user_can('edit_post', $post->ID)) {
63
- $json_api->error("You need to login with a user that has the 'edit_post' capacity for that post.");
64
  }
65
  if (!current_user_can('delete_posts')) {
66
- $json_api->error("You need to login with a user that has the 'delete_posts' capacity.");
67
  }
68
  if ($post->post_author != get_current_user_id() && !current_user_can('delete_other_posts')) {
69
- $json_api->error("You need to login with a user that has the 'delete_other_posts' capacity.");
70
  }
71
  if (!$json_api->query->nonce) {
72
- $json_api->error("You must include a 'nonce' value to update posts. Use the `get_nonce` Core API method.");
73
  }
74
  $nonce_id = $json_api->get_nonce_id('posts', 'delete_post');
75
  if (!wp_verify_nonce($json_api->query->nonce, $nonce_id)) {
76
- $json_api->error("Your 'nonce' value was incorrect. Use the 'get_nonce' API method.");
77
  }
78
  nocache_headers();
79
  wp_delete_post($post->ID);
9
  public function create_post() {
10
  global $json_api;
11
  if (!current_user_can('edit_posts')) {
12
+ $json_api->error("You need to login with a user that has 'edit_posts' capacity.", 403);
13
  }
14
  if (!$json_api->query->nonce) {
15
+ $json_api->error("You must include a 'nonce' value to create posts. Use the `get_nonce` Core API method.", 403);
16
  }
17
  $nonce_id = $json_api->get_nonce_id('posts', 'create_post');
18
  if (!wp_verify_nonce($json_api->query->nonce, $nonce_id)) {
19
+ $json_api->error("Your 'nonce' value was incorrect. Use the 'get_nonce' API method.", 403);
20
  }
21
  nocache_headers();
22
  $post = new JSON_API_Post();
23
  $id = $post->create($_REQUEST);
24
  if (empty($id)) {
25
+ $json_api->error("Could not create post.", 500);
26
  }
27
  return array(
28
  'post' => $post
36
  $json_api->error("Post not found.");
37
  }
38
  if (!current_user_can('edit_post', $post->ID)) {
39
+ $json_api->error("You need to login with a user that has the 'edit_post' capacity for that post.", 403);
40
  }
41
  if (!$json_api->query->nonce) {
42
+ $json_api->error("You must include a 'nonce' value to update posts. Use the `get_nonce` Core API method.", 403);
43
  }
44
  $nonce_id = $json_api->get_nonce_id('posts', 'update_post');
45
  if (!wp_verify_nonce($json_api->query->nonce, $nonce_id)) {
46
+ $json_api->error("Your 'nonce' value was incorrect. Use the 'get_nonce' API method.", 403);
47
  }
48
  nocache_headers();
49
  $post = new JSON_API_Post($post);
60
  $json_api->error("Post not found.");
61
  }
62
  if (!current_user_can('edit_post', $post->ID)) {
63
+ $json_api->error("You need to login with a user that has the 'edit_post' capacity for that post.", 403);
64
  }
65
  if (!current_user_can('delete_posts')) {
66
+ $json_api->error("You need to login with a user that has the 'delete_posts' capacity.", 403);
67
  }
68
  if ($post->post_author != get_current_user_id() && !current_user_can('delete_other_posts')) {
69
+ $json_api->error("You need to login with a user that has the 'delete_other_posts' capacity.", 403);
70
  }
71
  if (!$json_api->query->nonce) {
72
+ $json_api->error("You must include a 'nonce' value to update posts. Use the `get_nonce` Core API method.", 403);
73
  }
74
  $nonce_id = $json_api->get_nonce_id('posts', 'delete_post');
75
  if (!wp_verify_nonce($json_api->query->nonce, $nonce_id)) {
76
+ $json_api->error("Your 'nonce' value was incorrect. Use the 'get_nonce' API method.", 403);
77
  }
78
  nocache_headers();
79
  wp_delete_post($post->ID);
models/comment.php CHANGED
@@ -60,12 +60,12 @@ class JSON_API_Comment {
60
 
61
  function comment_closed() {
62
  global $json_api;
63
- $json_api->error("Post is closed for comments.");
64
  }
65
 
66
  function comment_on_draft() {
67
  global $json_api;
68
- $json_api->error("You cannot comment on unpublished posts.");
69
  }
70
 
71
  function comment_post_redirect() {
60
 
61
  function comment_closed() {
62
  global $json_api;
63
+ $json_api->error("Post is closed for comments.", 403);
64
  }
65
 
66
  function comment_on_draft() {
67
  global $json_api;
68
+ $json_api->error("You cannot comment on unpublished posts.", 403);
69
  }
70
 
71
  function comment_post_redirect() {
singletons/api.php CHANGED
@@ -379,10 +379,10 @@ class JSON_API {
379
  $wp_rewrite->flush_rules();
380
  }
381
 
382
- function error($message = 'Unknown error', $status = 'error') {
383
  $this->response->respond(array(
384
  'error' => $message
385
- ), $status);
386
  }
387
 
388
  function include_value($key) {
379
  $wp_rewrite->flush_rules();
380
  }
381
 
382
+ function error($message = 'Unknown error', $http_status = 404) {
383
  $this->response->respond(array(
384
  'error' => $message
385
+ ), 'error', $http_status);
386
  }
387
 
388
  function include_value($key) {
singletons/query.php CHANGED
@@ -168,7 +168,7 @@ class JSON_API_Query {
168
  return 'get_search_results';
169
  } else if (is_home()) {
170
  if (empty($_GET['json'])) {
171
- $json_api->error("Uknown method '$method'.");
172
  }
173
  return 'get_recent_posts';
174
  } else if (is_page()) {
168
  return 'get_search_results';
169
  } else if (is_home()) {
170
  if (empty($_GET['json'])) {
171
+ $json_api->error("Unknown method '$method'.");
172
  }
173
  return 'get_recent_posts';
174
  } else if (is_page()) {
singletons/response.php CHANGED
@@ -74,7 +74,7 @@ class JSON_API_Response {
74
  }
75
  }
76
 
77
- function respond($result, $status = 'ok') {
78
  global $json_api;
79
  $json = $this->get_json($result, $status);
80
  $status_redirect = "redirect_$status";
@@ -97,15 +97,15 @@ class JSON_API_Response {
97
  $this->callback($json_api->query->callback, $json);
98
  } else {
99
  // Output the result
100
- $this->output($json);
101
  }
102
  exit;
103
  }
104
 
105
- function output($result) {
106
  $charset = get_option('blog_charset');
107
  if (!headers_sent()) {
108
- header('HTTP/1.1 200 OK', true);
109
  header("Content-Type: application/json; charset=$charset", true);
110
  }
111
  echo $result;
@@ -114,7 +114,7 @@ class JSON_API_Response {
114
  function callback($callback, $result) {
115
  $charset = get_option('blog_charset');
116
  if (!headers_sent()) {
117
- header('HTTP/1.1 200 OK', true);
118
  header("Content-Type: application/javascript; charset=$charset", true);
119
  }
120
  echo "$callback($result)";
74
  }
75
  }
76
 
77
+ function respond($result, $status = 'ok', $http_status = 200) {
78
  global $json_api;
79
  $json = $this->get_json($result, $status);
80
  $status_redirect = "redirect_$status";
97
  $this->callback($json_api->query->callback, $json);
98
  } else {
99
  // Output the result
100
+ $this->output($json, $http_status);
101
  }
102
  exit;
103
  }
104
 
105
+ function output($result, $http_status = 200) {
106
  $charset = get_option('blog_charset');
107
  if (!headers_sent()) {
108
+ status_header($http_status);
109
  header("Content-Type: application/json; charset=$charset", true);
110
  }
111
  echo $result;
114
  function callback($callback, $result) {
115
  $charset = get_option('blog_charset');
116
  if (!headers_sent()) {
117
+ status_header(200);
118
  header("Content-Type: application/javascript; charset=$charset", true);
119
  }
120
  echo "$callback($result)";
trunk/controllers/core.php ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Controller name: Core
4
+ Controller description: Basic introspection methods
5
+ */
6
+
7
+ class JSON_API_Core_Controller {
8
+
9
+ public function info() {
10
+ global $json_api;
11
+ $php = '';
12
+ if (!empty($json_api->query->controller)) {
13
+ return $json_api->controller_info($json_api->query->controller);
14
+ } else {
15
+ $dir = json_api_dir();
16
+ if (file_exists("$dir/json-api.php")) {
17
+ $php = file_get_contents("$dir/json-api.php");
18
+ } else {
19
+ // Check one directory up, in case json-api.php was moved
20
+ $dir = dirname($dir);
21
+ if (file_exists("$dir/json-api.php")) {
22
+ $php = file_get_contents("$dir/json-api.php");
23
+ }
24
+ }
25
+ if (preg_match('/^\s*Version:\s*(.+)$/m', $php, $matches)) {
26
+ $version = $matches[1];
27
+ } else {
28
+ $version = '(Unknown)';
29
+ }
30
+ $active_controllers = explode(',', get_option('json_api_controllers', 'core'));
31
+ $controllers = array_intersect($json_api->get_controllers(), $active_controllers);
32
+ return array(
33
+ 'json_api_version' => $version,
34
+ 'controllers' => array_values($controllers)
35
+ );
36
+ }
37
+ }
38
+
39
+ public function get_recent_posts() {
40
+ global $json_api;
41
+ $posts = $json_api->introspector->get_posts();
42
+ return $this->posts_result($posts);
43
+ }
44
+
45
+ public function get_posts() {
46
+ global $json_api;
47
+ $url = parse_url($_SERVER['REQUEST_URI']);
48
+ $defaults = array(
49
+ 'ignore_sticky_posts' => true
50
+ );
51
+ $query = wp_parse_args($url['query']);
52
+ unset($query['json']);
53
+ unset($query['post_status']);
54
+ $query = array_merge($defaults, $query);
55
+ $posts = $json_api->introspector->get_posts($query);
56
+ $result = $this->posts_result($posts);
57
+ $result['query'] = $query;
58
+ return $result;
59
+ }
60
+
61
+ public function get_post() {
62
+ global $json_api, $post;
63
+ $post = $json_api->introspector->get_current_post();
64
+ if ($post) {
65
+ $previous = get_adjacent_post(false, '', true);
66
+ $next = get_adjacent_post(false, '', false);
67
+ $response = array(
68
+ 'post' => new JSON_API_Post($post)
69
+ );
70
+ if ($previous) {
71
+ $response['previous_url'] = get_permalink($previous->ID);
72
+ }
73
+ if ($next) {
74
+ $response['next_url'] = get_permalink($next->ID);
75
+ }
76
+ return $response;
77
+ } else {
78
+ $json_api->error("Not found.");
79
+ }
80
+ }
81
+
82
+ public function get_page() {
83
+ global $json_api;
84
+ extract($json_api->query->get(array('id', 'slug', 'page_id', 'page_slug', 'children')));
85
+ if ($id || $page_id) {
86
+ if (!$id) {
87
+ $id = $page_id;
88
+ }
89
+ $posts = $json_api->introspector->get_posts(array(
90
+ 'page_id' => $id
91
+ ));
92
+ } else if ($slug || $page_slug) {
93
+ if (!$slug) {
94
+ $slug = $page_slug;
95
+ }
96
+ $posts = $json_api->introspector->get_posts(array(
97
+ 'pagename' => $slug
98
+ ));
99
+ } else {
100
+ $json_api->error("Include 'id' or 'slug' var in your request.");
101
+ }
102
+
103
+ // Workaround for https://core.trac.wordpress.org/ticket/12647
104
+ if (empty($posts)) {
105
+ $url = $_SERVER['REQUEST_URI'];
106
+ $parsed_url = parse_url($url);
107
+ $path = $parsed_url['path'];
108
+ if (preg_match('#^http://[^/]+(/.+)$#', get_bloginfo('url'), $matches)) {
109
+ $blog_root = $matches[1];
110
+ $path = preg_replace("#^$blog_root#", '', $path);
111
+ }
112
+ if (substr($path, 0, 1) == '/') {
113
+ $path = substr($path, 1);
114
+ }
115
+ $posts = $json_api->introspector->get_posts(array('pagename' => $path));
116
+ }
117
+
118
+ if (count($posts) == 1) {
119
+ if (!empty($children)) {
120
+ $json_api->introspector->attach_child_posts($posts[0]);
121
+ }
122
+ return array(
123
+ 'page' => $posts[0]
124
+ );
125
+ } else {
126
+ $json_api->error("Not found.");
127
+ }
128
+ }
129
+
130
+ public function get_date_posts() {
131
+ global $json_api;
132
+ if ($json_api->query->date) {
133
+ $date = preg_replace('/\D/', '', $json_api->query->date);
134
+ if (!preg_match('/^\d{4}(\d{2})?(\d{2})?$/', $date)) {
135
+ $json_api->error("Specify a date var in one of 'YYYY' or 'YYYY-MM' or 'YYYY-MM-DD' formats.");
136
+ }
137
+ $request = array('year' => substr($date, 0, 4));
138
+ if (strlen($date) > 4) {
139
+ $request['monthnum'] = (int) substr($date, 4, 2);
140
+ }
141
+ if (strlen($date) > 6) {
142
+ $request['day'] = (int) substr($date, 6, 2);
143
+ }
144
+ $posts = $json_api->introspector->get_posts($request);
145
+ } else {
146
+ $json_api->error("Include 'date' var in your request.");
147
+ }
148
+ return $this->posts_result($posts);
149
+ }
150
+
151
+ public function get_category_posts() {
152
+ global $json_api;
153
+ $category = $json_api->introspector->get_current_category();
154
+ if (!$category) {
155
+ $json_api->error("Not found.");
156
+ }
157
+ $posts = $json_api->introspector->get_posts(array(
158
+ 'cat' => $category->id
159
+ ));
160
+ return $this->posts_object_result($posts, $category);
161
+ }
162
+
163
+ public function get_tag_posts() {
164
+ global $json_api;
165
+ $tag = $json_api->introspector->get_current_tag();
166
+ if (!$tag) {
167
+ $json_api->error("Not found.");
168
+ }
169
+ $posts = $json_api->introspector->get_posts(array(
170
+ 'tag' => $tag->slug
171
+ ));
172
+ return $this->posts_object_result($posts, $tag);
173
+ }
174
+
175
+ public function get_author_posts() {
176
+ global $json_api;
177
+ $author = $json_api->introspector->get_current_author();
178
+ if (!$author) {
179
+ $json_api->error("Not found.");
180
+ }
181
+ $posts = $json_api->introspector->get_posts(array(
182
+ 'author' => $author->id
183
+ ));
184
+ return $this->posts_object_result($posts, $author);
185
+ }
186
+
187
+ public function get_search_results() {
188
+ global $json_api;
189
+ if ($json_api->query->search) {
190
+ $posts = $json_api->introspector->get_posts(array(
191
+ 's' => $json_api->query->search
192
+ ));
193
+ } else {
194
+ $json_api->error("Include 'search' var in your request.");
195
+ }
196
+ return $this->posts_result($posts);
197
+ }
198
+
199
+ public function get_date_index() {
200
+ global $json_api;
201
+ $permalinks = $json_api->introspector->get_date_archive_permalinks();
202
+ $tree = $json_api->introspector->get_date_archive_tree($permalinks);
203
+ return array(
204
+ 'permalinks' => $permalinks,
205
+ 'tree' => $tree
206
+ );
207
+ }
208
+
209
+ public function get_category_index() {
210
+ global $json_api;
211
+ $args = null;
212
+ if (!empty($json_api->query->parent)) {
213
+ $args = array(
214
+ 'parent' => $json_api->query->parent
215
+ );
216
+ }
217
+ $categories = $json_api->introspector->get_categories($args);
218
+ return array(
219
+ 'count' => count($categories),
220
+ 'categories' => $categories
221
+ );
222
+ }
223
+
224
+ public function get_tag_index() {
225
+ global $json_api;
226
+ $tags = $json_api->introspector->get_tags();
227
+ return array(
228
+ 'count' => count($tags),
229
+ 'tags' => $tags
230
+ );
231
+ }
232
+
233
+ public function get_author_index() {
234
+ global $json_api;
235
+ $authors = $json_api->introspector->get_authors();
236
+ return array(
237
+ 'count' => count($authors),
238
+ 'authors' => array_values($authors)
239
+ );
240
+ }
241
+
242
+ public function get_page_index() {
243
+ global $json_api;
244
+ $pages = array();
245
+ $post_type = $json_api->query->post_type ? $json_api->query->post_type : 'page';
246
+
247
+ // Thanks to blinder for the fix!
248
+ $numberposts = empty($json_api->query->count) ? -1 : $json_api->query->count;
249
+ $wp_posts = get_posts(array(
250
+ 'post_type' => $post_type,
251
+ 'post_parent' => 0,
252
+ 'order' => 'ASC',
253
+ 'orderby' => 'menu_order',
254
+ 'numberposts' => $numberposts
255
+ ));
256
+ foreach ($wp_posts as $wp_post) {
257
+ $pages[] = new JSON_API_Post($wp_post);
258
+ }
259
+ foreach ($pages as $page) {
260
+ $json_api->introspector->attach_child_posts($page);
261
+ }
262
+ return array(
263
+ 'pages' => $pages
264
+ );
265
+ }
266
+
267
+ public function get_nonce() {
268
+ global $json_api;
269
+ extract($json_api->query->get(array('controller', 'method')));
270
+ if ($controller && $method) {
271
+ $controller = strtolower($controller);
272
+ if (!in_array($controller, $json_api->get_controllers())) {
273
+ $json_api->error("Unknown controller '$controller'.");
274
+ }
275
+ require_once $json_api->controller_path($controller);
276
+ if (!method_exists($json_api->controller_class($controller), $method)) {
277
+ $json_api->error("Unknown method '$method'.");
278
+ }
279
+ $nonce_id = $json_api->get_nonce_id($controller, $method);
280
+ return array(
281
+ 'controller' => $controller,
282
+ 'method' => $method,
283
+ 'nonce' => wp_create_nonce($nonce_id)
284
+ );
285
+ } else {
286
+ $json_api->error("Include 'controller' and 'method' vars in your request.");
287
+ }
288
+ }
289
+
290
+ protected function get_object_posts($object, $id_var, $slug_var) {
291
+ global $json_api;
292
+ $object_id = "{$type}_id";
293
+ $object_slug = "{$type}_slug";
294
+ extract($json_api->query->get(array('id', 'slug', $object_id, $object_slug)));
295
+ if ($id || $$object_id) {
296
+ if (!$id) {
297
+ $id = $$object_id;
298
+ }
299
+ $posts = $json_api->introspector->get_posts(array(
300
+ $id_var => $id
301
+ ));
302
+ } else if ($slug || $$object_slug) {
303
+ if (!$slug) {
304
+ $slug = $$object_slug;
305
+ }
306
+ $posts = $json_api->introspector->get_posts(array(
307
+ $slug_var => $slug
308
+ ));
309
+ } else {
310
+ $json_api->error("No $type specified. Include 'id' or 'slug' var in your request.");
311
+ }
312
+ return $posts;
313
+ }
314
+
315
+ protected function posts_result($posts) {
316
+ global $wp_query;
317
+ return array(
318
+ 'count' => count($posts),
319
+ 'count_total' => (int) $wp_query->found_posts,
320
+ 'pages' => $wp_query->max_num_pages,
321
+ 'posts' => $posts
322
+ );
323
+ }
324
+
325
+ protected function posts_object_result($posts, $object) {
326
+ global $wp_query;
327
+ // Convert something like "JSON_API_Category" into "category"
328
+ $object_key = strtolower(substr(get_class($object), 9));
329
+ return array(
330
+ 'count' => count($posts),
331
+ 'pages' => (int) $wp_query->max_num_pages,
332
+ $object_key => $object,
333
+ 'posts' => $posts
334
+ );
335
+ }
336
+
337
+ }
338
+
339
+ ?>
trunk/controllers/posts.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Controller name: Posts
4
+ Controller description: Data manipulation methods for posts
5
+ */
6
+
7
+ class JSON_API_Posts_Controller {
8
+
9
+ public function create_post() {
10
+ global $json_api;
11
+ if (!current_user_can('edit_posts')) {
12
+ $json_api->error("You need to login with a user that has 'edit_posts' capacity.", 403);
13
+ }
14
+ if (!$json_api->query->nonce) {
15
+ $json_api->error("You must include a 'nonce' value to create posts. Use the `get_nonce` Core API method.", 403);
16
+ }
17
+ $nonce_id = $json_api->get_nonce_id('posts', 'create_post');
18
+ if (!wp_verify_nonce($json_api->query->nonce, $nonce_id)) {
19
+ $json_api->error("Your 'nonce' value was incorrect. Use the 'get_nonce' API method.", 403);
20
+ }
21
+ nocache_headers();
22
+ $post = new JSON_API_Post();
23
+ $id = $post->create($_REQUEST);
24
+ if (empty($id)) {
25
+ $json_api->error("Could not create post.", 500);
26
+ }
27
+ return array(
28
+ 'post' => $post
29
+ );
30
+ }
31
+
32
+ public function update_post() {
33
+ global $json_api;
34
+ $post = $json_api->introspector->get_current_post();
35
+ if (empty($post)) {
36
+ $json_api->error("Post not found.");
37
+ }
38
+ if (!current_user_can('edit_post', $post->ID)) {
39
+ $json_api->error("You need to login with a user that has the 'edit_post' capacity for that post.", 403);
40
+ }
41
+ if (!$json_api->query->nonce) {
42
+ $json_api->error("You must include a 'nonce' value to update posts. Use the `get_nonce` Core API method.", 403);
43
+ }
44
+ $nonce_id = $json_api->get_nonce_id('posts', 'update_post');
45
+ if (!wp_verify_nonce($json_api->query->nonce, $nonce_id)) {
46
+ $json_api->error("Your 'nonce' value was incorrect. Use the 'get_nonce' API method.", 403);
47
+ }
48
+ nocache_headers();
49
+ $post = new JSON_API_Post($post);
50
+ $post->update($_REQUEST);
51
+ return array(
52
+ 'post' => $post
53
+ );
54
+ }
55
+
56
+ public function delete_post() {
57
+ global $json_api;
58
+ $post = $json_api->introspector->get_current_post();
59
+ if (empty($post)) {
60
+ $json_api->error("Post not found.");
61
+ }
62
+ if (!current_user_can('edit_post', $post->ID)) {
63
+ $json_api->error("You need to login with a user that has the 'edit_post' capacity for that post.", 403);
64
+ }
65
+ if (!current_user_can('delete_posts')) {
66
+ $json_api->error("You need to login with a user that has the 'delete_posts' capacity.", 403);
67
+ }
68
+ if ($post->post_author != get_current_user_id() && !current_user_can('delete_other_posts')) {
69
+ $json_api->error("You need to login with a user that has the 'delete_other_posts' capacity.", 403);
70
+ }
71
+ if (!$json_api->query->nonce) {
72
+ $json_api->error("You must include a 'nonce' value to update posts. Use the `get_nonce` Core API method.", 403);
73
+ }
74
+ $nonce_id = $json_api->get_nonce_id('posts', 'delete_post');
75
+ if (!wp_verify_nonce($json_api->query->nonce, $nonce_id)) {
76
+ $json_api->error("Your 'nonce' value was incorrect. Use the 'get_nonce' API method.", 403);
77
+ }
78
+ nocache_headers();
79
+ wp_delete_post($post->ID);
80
+ return array();
81
+ }
82
+
83
+ }
84
+
85
+ ?>
trunk/controllers/respond.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Controller name: Respond
4
+ Controller description: Comment/trackback submission methods
5
+ */
6
+
7
+ class JSON_API_Respond_Controller {
8
+
9
+ function submit_comment() {
10
+ global $json_api;
11
+ nocache_headers();
12
+ if (empty($_REQUEST['post_id'])) {
13
+ $json_api->error("No post specified. Include 'post_id' var in your request.");
14
+ } else if (empty($_REQUEST['name']) ||
15
+ empty($_REQUEST['email']) ||
16
+ empty($_REQUEST['content'])) {
17
+ $json_api->error("Please include all required arguments (name, email, content).");
18
+ } else if (!is_email($_REQUEST['email'])) {
19
+ $json_api->error("Please enter a valid email address.");
20
+ }
21
+ $pending = new JSON_API_Comment();
22
+ return $pending->handle_submission();
23
+ }
24
+
25
+ }
26
+
27
+ ?>
trunk/controllers/widgets.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Controller name: Widgets
4
+ Controller description: Retrieve sidebar widgets
5
+ */
6
+
7
+ class JSON_API_Widgets_Controller {
8
+
9
+ function get_sidebar() {
10
+ global $json_api;
11
+ $index = @$_REQUEST['sidebar_id'];
12
+ if (empty($_REQUEST['sidebar_id'])) {
13
+ $json_api->error("No sidebar specified. Include 'sidebar_id' var in your request.");
14
+ } else if (!is_active_sidebar($index)) {
15
+ $json_api->error("Sidebar '$index' is not active.");
16
+ }
17
+
18
+ $widget_params = array(
19
+ 'before_widget',
20
+ 'after_widget',
21
+ 'before_title',
22
+ 'after_title'
23
+ );
24
+ $json_api_params = array();
25
+ foreach ($widget_params as $param) {
26
+ if (isset($_REQUEST[$param])) {
27
+ $json_api_params[$param] = $_REQUEST[$param];
28
+ }
29
+ }
30
+
31
+ $widgets = array();
32
+
33
+ global $wp_registered_sidebars, $wp_registered_widgets;
34
+
35
+ if ( is_int($index) ) {
36
+ $index = "sidebar-$index";
37
+ } else {
38
+ $index = sanitize_title($index);
39
+ foreach ( (array) $wp_registered_sidebars as $key => $value ) {
40
+ if ( sanitize_title($value['name']) == $index ) {
41
+ $index = $key;
42
+ break;
43
+ }
44
+ }
45
+ }
46
+
47
+ $sidebars_widgets = wp_get_sidebars_widgets();
48
+
49
+ if ( empty($wp_registered_sidebars[$index]) || !array_key_exists($index, $sidebars_widgets) || !is_array($sidebars_widgets[$index]) || empty($sidebars_widgets[$index]) )
50
+ return false;
51
+
52
+ $sidebar = $wp_registered_sidebars[$index];
53
+
54
+ $did_one = false;
55
+ foreach ( (array) $sidebars_widgets[$index] as $id ) {
56
+
57
+ if ( !isset($wp_registered_widgets[$id]) ) continue;
58
+
59
+ $params = array_merge(
60
+ array( array_merge( $sidebar, array('widget_id' => $id, 'widget_name' => $wp_registered_widgets[$id]['name']), $json_api_params ) ),
61
+ (array) $wp_registered_widgets[$id]['params']
62
+ );
63
+
64
+
65
+ // Substitute HTML id and class attributes into before_widget
66
+ $classname_ = '';
67
+ foreach ( (array) $wp_registered_widgets[$id]['classname'] as $cn ) {
68
+ if ( is_string($cn) )
69
+ $classname_ .= '_' . $cn;
70
+ elseif ( is_object($cn) )
71
+ $classname_ .= '_' . get_class($cn);
72
+ }
73
+ $classname_ = ltrim($classname_, '_');
74
+ $params[0]['before_widget'] = sprintf($params[0]['before_widget'], $id, $classname_);
75
+
76
+ $params = apply_filters( 'dynamic_sidebar_params', $params );
77
+
78
+ $callback = $wp_registered_widgets[$id]['callback'];
79
+
80
+ do_action( 'dynamic_sidebar', $wp_registered_widgets[$id] );
81
+
82
+ if ( is_callable($callback) ) {
83
+ ob_start();
84
+ $object = $callback[0];
85
+ $settings = $object->get_settings();
86
+ $widget_params = $wp_registered_widgets[$id]['params'];
87
+ $number = $widget_params[0]['number'];
88
+ $instance = $settings[$number];
89
+ call_user_func_array($callback, $params);
90
+ $widgets[] = array(
91
+ 'id' => $id,
92
+ 'widget' => trim(ob_get_contents()),
93
+ 'params' => $params[0],
94
+ 'instance' => $instance
95
+ );
96
+ ob_end_clean();
97
+ }
98
+ }
99
+
100
+ return array(
101
+ 'sidebar_id' => $index,
102
+ 'widgets' => $widgets
103
+ );
104
+ }
105
+
106
+ }
107
+
108
+ ?>
trunk/json-api.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: JSON API
4
+ Plugin URI: http://wordpress.org/plugins/json-api/
5
+ Description: A RESTful API for WordPress
6
+ Version: 1.1.1
7
+ Author: Dan Phiffer
8
+ Author URI: http://phiffer.org/
9
+ */
10
+
11
+ $dir = json_api_dir();
12
+ @include_once "$dir/singletons/api.php";
13
+ @include_once "$dir/singletons/query.php";
14
+ @include_once "$dir/singletons/introspector.php";
15
+ @include_once "$dir/singletons/response.php";
16
+ @include_once "$dir/models/post.php";
17
+ @include_once "$dir/models/comment.php";
18
+ @include_once "$dir/models/category.php";
19
+ @include_once "$dir/models/tag.php";
20
+ @include_once "$dir/models/author.php";
21
+ @include_once "$dir/models/attachment.php";
22
+
23
+ function json_api_init() {
24
+ global $json_api;
25
+ if (phpversion() < 5) {
26
+ add_action('admin_notices', 'json_api_php_version_warning');
27
+ return;
28
+ }
29
+ if (!class_exists('JSON_API')) {
30
+ add_action('admin_notices', 'json_api_class_warning');
31
+ return;
32
+ }
33
+ add_filter('rewrite_rules_array', 'json_api_rewrites');
34
+ $json_api = new JSON_API();
35
+ }
36
+
37
+ function json_api_php_version_warning() {
38
+ echo "<div id=\"json-api-warning\" class=\"updated fade\"><p>Sorry, JSON API requires PHP version 5.0 or greater.</p></div>";
39
+ }
40
+
41
+ function json_api_class_warning() {
42
+ echo "<div id=\"json-api-warning\" class=\"updated fade\"><p>Oops, JSON_API class not found. If you've defined a JSON_API_DIR constant, double check that the path is correct.</p></div>";
43
+ }
44
+
45
+ function json_api_activation() {
46
+ // Add the rewrite rule on activation
47
+ global $wp_rewrite;
48
+ add_filter('rewrite_rules_array', 'json_api_rewrites');
49
+ $wp_rewrite->flush_rules();
50
+ }
51
+
52
+ function json_api_deactivation() {
53
+ // Remove the rewrite rule on deactivation
54
+ global $wp_rewrite;
55
+ $wp_rewrite->flush_rules();
56
+ }
57
+
58
+ function json_api_rewrites($wp_rules) {
59
+ $base = get_option('json_api_base', 'api');
60
+ if (empty($base)) {
61
+ return $wp_rules;
62
+ }
63
+ $json_api_rules = array(
64
+ "$base\$" => 'index.php?json=info',
65
+ "$base/(.+)\$" => 'index.php?json=$matches[1]'
66
+ );
67
+ return array_merge($json_api_rules, $wp_rules);
68
+ }
69
+
70
+ function json_api_dir() {
71
+ if (defined('JSON_API_DIR') && file_exists(JSON_API_DIR)) {
72
+ return JSON_API_DIR;
73
+ } else {
74
+ return dirname(__FILE__);
75
+ }
76
+ }
77
+
78
+ // Add initialization and activation hooks
79
+ add_action('init', 'json_api_init');
80
+ register_activation_hook("$dir/json-api.php", 'json_api_activation');
81
+ register_deactivation_hook("$dir/json-api.php", 'json_api_deactivation');
82
+
83
+ ?>
trunk/library/JSON.php ADDED
@@ -0,0 +1,933 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+ /**
4
+ * Converts to and from JSON format.
5
+ *
6
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
7
+ * format. It is easy for humans to read and write. It is easy for machines
8
+ * to parse and generate. It is based on a subset of the JavaScript
9
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
10
+ * This feature can also be found in Python. JSON is a text format that is
11
+ * completely language independent but uses conventions that are familiar
12
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
13
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
14
+ * ideal data-interchange language.
15
+ *
16
+ * This package provides a simple encoder and decoder for JSON notation. It
17
+ * is intended for use with client-side Javascript applications that make
18
+ * use of HTTPRequest to perform server communication functions - data can
19
+ * be encoded into JSON notation for use in a client-side javascript, or
20
+ * decoded from incoming Javascript requests. JSON format is native to
21
+ * Javascript, and can be directly eval()'ed with no further parsing
22
+ * overhead
23
+ *
24
+ * All strings should be in ASCII or UTF-8 format!
25
+ *
26
+ * LICENSE: Redistribution and use in source and binary forms, with or
27
+ * without modification, are permitted provided that the following
28
+ * conditions are met: Redistributions of source code must retain the
29
+ * above copyright notice, this list of conditions and the following
30
+ * disclaimer. Redistributions in binary form must reproduce the above
31
+ * copyright notice, this list of conditions and the following disclaimer
32
+ * in the documentation and/or other materials provided with the
33
+ * distribution.
34
+ *
35
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
36
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
37
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
38
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
39
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
40
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
41
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
42
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
43
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
44
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
45
+ * DAMAGE.
46
+ *
47
+ * @category
48
+ * @package Services_JSON
49
+ * @author Michal Migurski <mike-json@teczno.com>
50
+ * @author Matt Knapp <mdknapp[at]gmail[dot]com>
51
+ * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
52
+ * @copyright 2005 Michal Migurski
53
+ * @version CVS: $Id: JSON.php 305040 2010-11-02 23:19:03Z alan_k $
54
+ * @license http://www.opensource.org/licenses/bsd-license.php
55
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
56
+ */
57
+
58
+ /**
59
+ * Marker constant for Services_JSON::decode(), used to flag stack state
60
+ */
61
+ define('SERVICES_JSON_SLICE', 1);
62
+
63
+ /**
64
+ * Marker constant for Services_JSON::decode(), used to flag stack state
65
+ */
66
+ define('SERVICES_JSON_IN_STR', 2);
67
+
68
+ /**
69
+ * Marker constant for Services_JSON::decode(), used to flag stack state
70
+ */
71
+ define('SERVICES_JSON_IN_ARR', 3);
72
+
73
+ /**
74
+ * Marker constant for Services_JSON::decode(), used to flag stack state
75
+ */
76
+ define('SERVICES_JSON_IN_OBJ', 4);
77
+
78
+ /**
79
+ * Marker constant for Services_JSON::decode(), used to flag stack state
80
+ */
81
+ define('SERVICES_JSON_IN_CMT', 5);
82
+
83
+ /**
84
+ * Behavior switch for Services_JSON::decode()
85
+ */
86
+ define('SERVICES_JSON_LOOSE_TYPE', 16);
87
+
88
+ /**
89
+ * Behavior switch for Services_JSON::decode()
90
+ */
91
+ define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
92
+
93
+ /**
94
+ * Behavior switch for Services_JSON::decode()
95
+ */
96
+ define('SERVICES_JSON_USE_TO_JSON', 64);
97
+
98
+ /**
99
+ * Converts to and from JSON format.
100
+ *
101
+ * Brief example of use:
102
+ *
103
+ * <code>
104
+ * // create a new instance of Services_JSON
105
+ * $json = new Services_JSON();
106
+ *
107
+ * // convert a complexe value to JSON notation, and send it to the browser
108
+ * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
109
+ * $output = $json->encode($value);
110
+ *
111
+ * print($output);
112
+ * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
113
+ *
114
+ * // accept incoming POST data, assumed to be in JSON notation
115
+ * $input = file_get_contents('php://input', 1000000);
116
+ * $value = $json->decode($input);
117
+ * </code>
118
+ */
119
+ class Services_JSON
120
+ {
121
+ /**
122
+ * constructs a new JSON instance
123
+ *
124
+ * @param int $use object behavior flags; combine with boolean-OR
125
+ *
126
+ * possible values:
127
+ * - SERVICES_JSON_LOOSE_TYPE: loose typing.
128
+ * "{...}" syntax creates associative arrays
129
+ * instead of objects in decode().
130
+ * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
131
+ * Values which can't be encoded (e.g. resources)
132
+ * appear as NULL instead of throwing errors.
133
+ * By default, a deeply-nested resource will
134
+ * bubble up with an error, so all return values
135
+ * from encode() should be checked with isError()
136
+ * - SERVICES_JSON_USE_TO_JSON: call toJSON when serializing objects
137
+ * It serializes the return value from the toJSON call rather
138
+ * than the object it'self, toJSON can return associative arrays,
139
+ * strings or numbers, if you return an object, make sure it does
140
+ * not have a toJSON method, otherwise an error will occur.
141
+ */
142
+ function Services_JSON($use = 0)
143
+ {
144
+ $this->use = $use;
145
+ $this->_mb_strlen = function_exists('mb_strlen');
146
+ $this->_mb_convert_encoding = function_exists('mb_convert_encoding');
147
+ $this->_mb_substr = function_exists('mb_substr');
148
+ }
149
+ // private - cache the mbstring lookup results..
150
+ var $_mb_strlen = false;
151
+ var $_mb_substr = false;
152
+ var $_mb_convert_encoding = false;
153
+
154
+ /**
155
+ * convert a string from one UTF-16 char to one UTF-8 char
156