From 7746611f20554c92ad0213de196e880e41c3cc4e Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 5 Jul 2010 14:12:14 -0400 Subject: [PATCH 01/13] create an index on user.created so sitemap generation is faster --- plugins/Sitemap/SitemapPlugin.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/Sitemap/SitemapPlugin.php b/plugins/Sitemap/SitemapPlugin.php index d4d295237d..b6d3b1ad33 100644 --- a/plugins/Sitemap/SitemapPlugin.php +++ b/plugins/Sitemap/SitemapPlugin.php @@ -202,6 +202,12 @@ class SitemapPlugin extends Plugin null, false), new ColumnDef('modified', 'timestamp'))); + $userCreated = $schema->getColumnDef('user', 'created'); + + if (empty($userCreated) || $userCreated->key != 'MUL') { + $schema->createIndex('user', 'created'); + } + return true; } From 4c5098cd32599a2c376beaadb43cd9d471477c90 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 8 Jul 2010 21:17:10 +0000 Subject: [PATCH 02/13] Handle the case where a screen name has shifted from one Twitter ID to another --- classes/Foreign_user.php | 16 ++++++++++++++++ plugins/TwitterBridge/twitter.php | 21 +++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/classes/Foreign_user.php b/classes/Foreign_user.php index 0dd94ffb99..e98a16064a 100644 --- a/classes/Foreign_user.php +++ b/classes/Foreign_user.php @@ -39,6 +39,22 @@ class Foreign_user extends Memcached_DataObject return null; } + static function getByNickname($nickname, $service) + { + if (empty($nickname) || empty($service)) { + return null; + } else { + $fuser = new Foreign_user(); + $fuser->service = $service; + $fuser->nickname = $nickname; + $fuser->limit(1); + + $result = $fuser->find(true); + + return empty($result) ? null : $fuser; + } + } + function updateKeys(&$orig) { $this->_connect(); diff --git a/plugins/TwitterBridge/twitter.php b/plugins/TwitterBridge/twitter.php index 896eee2dac..306ba2442a 100644 --- a/plugins/TwitterBridge/twitter.php +++ b/plugins/TwitterBridge/twitter.php @@ -75,8 +75,6 @@ function save_twitter_user($twitter_id, $screen_name) if (!empty($fuser)) { - $result = true; - // Delete old record if Twitter user changed screen name if ($fuser->nickname != $screen_name) { @@ -88,6 +86,25 @@ function save_twitter_user($twitter_id, $screen_name) $screen_name, $oldname)); } + + } else { + + // Kill any old, invalid records for this screen name + + $fuser = Foreign_user::getByNickname($screen_name, TWITTER_SERVICE); + + if (!empty($fuser)) { + $fuser->delete(); + common_log( + LOG_INFO, + sprintf( + 'Twitter bridge - deteted old record for Twitter ' . + 'screen name "%s" belonging to Twitter ID %d.', + $screen_name, + $fuser->id + ) + ); + } } return add_twitter_user($twitter_id, $screen_name); From 869ebb7dc7a5f4241b94512646cdd7ae89e35962 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 9 Jul 2010 17:50:14 -0400 Subject: [PATCH 03/13] script to fill counts table from command line --- plugins/Sitemap/scripts/updatecounts.php | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 plugins/Sitemap/scripts/updatecounts.php diff --git a/plugins/Sitemap/scripts/updatecounts.php b/plugins/Sitemap/scripts/updatecounts.php new file mode 100644 index 0000000000..15c9864187 --- /dev/null +++ b/plugins/Sitemap/scripts/updatecounts.php @@ -0,0 +1,36 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); + +$helptext = << Date: Sat, 10 Jul 2010 09:58:27 -0400 Subject: [PATCH 04/13] remove debugging statement and add a bit of output to updatecounts --- plugins/Sitemap/scripts/updatecounts.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Sitemap/scripts/updatecounts.php b/plugins/Sitemap/scripts/updatecounts.php index 15c9864187..91bc0ac4e9 100644 --- a/plugins/Sitemap/scripts/updatecounts.php +++ b/plugins/Sitemap/scripts/updatecounts.php @@ -26,11 +26,11 @@ Update the notice and user counts cached in the database. END_OF_UPDATECOUNTS_HELP; -echo "INSTALLDIR=".INSTALLDIR."\n"; - require_once INSTALLDIR.'/scripts/commandline.inc'; // Will fill the cache $userCounts = Sitemap_user_count::getAll(); $noticeCounts = Sitemap_notice_count::getAll(); + +echo "Done.\n"; From 1339f1f908acf2ad69928494f7dfc5c09c5fb211 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 12 Jul 2010 14:22:36 -0700 Subject: [PATCH 05/13] Commit hubprepqueuehandler.php -- fix for OStatus bulk output. --- plugins/OStatus/lib/hubprepqueuehandler.php | 87 +++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 plugins/OStatus/lib/hubprepqueuehandler.php diff --git a/plugins/OStatus/lib/hubprepqueuehandler.php b/plugins/OStatus/lib/hubprepqueuehandler.php new file mode 100644 index 0000000000..0d585938f4 --- /dev/null +++ b/plugins/OStatus/lib/hubprepqueuehandler.php @@ -0,0 +1,87 @@ +. + */ + +/** + * When we have a large batch of PuSH consumers, we break the data set + * into smaller chunks. Enqueue final destinations... + * + * @package Hub + * @author Brion Vibber + */ +class HubPrepQueueHandler extends QueueHandler +{ + // Enqueue this many low-level distributions before re-queueing the rest + // of the batch to be processed later. Helps to keep latency down for other + // things happening during a particularly long OStatus delivery session. + // + // [Could probably ditch this if we had working message delivery priorities + // for queueing, but this isn't supported in ActiveMQ 5.3.] + const ROLLING_BATCH = 20; + + function transport() + { + return 'hubprep'; + } + + function handle($data) + { + $topic = $data['topic']; + $atom = $data['atom']; + $pushCallbacks = $data['pushCallbacks']; + + assert(is_string($atom)); + assert(is_string($topic)); + assert(is_array($pushCallbacks)); + + // Set up distribution for the first n subscribing sites... + // If we encounter an uncatchable error, queue handling should + // automatically re-run the batch, which could lead to some dupe + // distributions. + // + // Worst case is if one of these hubprep entries dies too many + // times and gets dropped; the rest of the batch won't get processed. + try { + $n = 0; + while (count($pushCallbacks) && $n < self::ROLLING_BATCH) { + $n++; + $callback = array_shift($pushCallbacks); + $sub = HubSub::staticGet($topic, $callback); + if (!$sub) { + common_log(LOG_ERR, "Skipping PuSH delivery for deleted(?) consumer $callback on $topic"); + continue; + } + + $sub->distribute($atom); + } + } catch (Exception $e) { + common_log(LOG_ERR, "Exception during PuSH batch out: " . + $e->getMessage() . + " prepping $topic to $callback"); + } + + // And re-queue the rest of the batch! + if (count($pushCallbacks) > 0) { + $sub = new HubSub(); + $sub->topic = $topic; + $sub->bulkDistribute($atom, $pushCallbacks); + } + + return true; + } +} From ab149755b62aaf7959a97e4a425e20aebf4c92cc Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 13 Jul 2010 10:42:06 -0400 Subject: [PATCH 06/13] handle notices without profiles better in RSS output --- lib/apiaction.php | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/apiaction.php b/lib/apiaction.php index 8de13a62da..16dd878142 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -461,6 +461,11 @@ class ApiAction extends Action function twitterRssEntryArray($notice) { $profile = $notice->getProfile(); + + if (empty($profile)) { + throw new ServerException(sprintf(_('No such profile: %d'), $notice->profile_id)); + } + $entry = array(); // We trim() to avoid extraneous whitespace in the output @@ -789,13 +794,23 @@ class ApiAction extends Action if (is_array($notice)) { foreach ($notice as $n) { - $entry = $this->twitterRssEntryArray($n); - $this->showTwitterRssItem($entry); + try { + $entry = $this->twitterRssEntryArray($n); + $this->showTwitterRssItem($entry); + } catch (Exception $e) { + common_log(LOG_ERR, "Error with notice {$n->id}: " . $e->getMessage()); + // continue on exceptions + } } } else { while ($notice->fetch()) { - $entry = $this->twitterRssEntryArray($notice); - $this->showTwitterRssItem($entry); + try { + $entry = $this->twitterRssEntryArray($notice); + $this->showTwitterRssItem($entry); + } catch (Exception $e) { + common_log(LOG_ERR, "Error with notice {$n->id}: " . $e->getMessage()); + // continue on exceptions + } } } From dfd65a4290c7c2f6137d5508597189c4202bfaee Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 13 Jul 2010 10:51:25 -0400 Subject: [PATCH 07/13] push exception on missing profile down to Notice::getProfile() --- classes/Notice.php | 8 +++++++- lib/apiaction.php | 8 ++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/classes/Notice.php b/classes/Notice.php index 482bc550b9..ae7e2e5402 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -90,7 +90,13 @@ class Notice extends Memcached_DataObject function getProfile() { - return Profile::staticGet('id', $this->profile_id); + $profile = Profile::staticGet('id', $this->profile_id); + + if (empty($profile)) { + throw new ServerException(sprintf(_('No such profile (%d) for notice (%d)'), $this->profile_id, $this->id)); + } + + return $profile; } function delete() diff --git a/lib/apiaction.php b/lib/apiaction.php index 16dd878142..01985f0db3 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -462,10 +462,6 @@ class ApiAction extends Action { $profile = $notice->getProfile(); - if (empty($profile)) { - throw new ServerException(sprintf(_('No such profile: %d'), $notice->profile_id)); - } - $entry = array(); // We trim() to avoid extraneous whitespace in the output @@ -798,7 +794,7 @@ class ApiAction extends Action $entry = $this->twitterRssEntryArray($n); $this->showTwitterRssItem($entry); } catch (Exception $e) { - common_log(LOG_ERR, "Error with notice {$n->id}: " . $e->getMessage()); + common_log(LOG_ERR, $e->getMessage()); // continue on exceptions } } @@ -808,7 +804,7 @@ class ApiAction extends Action $entry = $this->twitterRssEntryArray($notice); $this->showTwitterRssItem($entry); } catch (Exception $e) { - common_log(LOG_ERR, "Error with notice {$n->id}: " . $e->getMessage()); + common_log(LOG_ERR, $e->getMessage()); // continue on exceptions } } From 6968c96b44d17295ceb229af2385449400f4976a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 13 Jul 2010 10:53:47 -0400 Subject: [PATCH 08/13] log exceptions in atom feeds, but don't let them kill the output --- lib/atomnoticefeed.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/atomnoticefeed.php b/lib/atomnoticefeed.php index 6ed803ce4e..b882172918 100644 --- a/lib/atomnoticefeed.php +++ b/lib/atomnoticefeed.php @@ -125,12 +125,17 @@ class AtomNoticeFeed extends Atom10Feed */ function addEntryFromNotice($notice) { - $source = $this->showSource(); - $author = $this->showAuthor(); + try { + $source = $this->showSource(); + $author = $this->showAuthor(); - $cur = empty($this->cur) ? common_current_user() : $this->cur; + $cur = empty($this->cur) ? common_current_user() : $this->cur; - $this->addEntryRaw($notice->asAtomEntry(false, $source, $author, $cur)); + $this->addEntryRaw($notice->asAtomEntry(false, $source, $author, $cur)); + } catch (Exception $e) { + common_log(LOG_ERR, $e->getMessage()); + // we continue on exceptions + } } function showSource() From d706a3e21ba88afc9e7a19652aa19eb4e8c54cd2 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 13 Jul 2010 10:59:01 -0400 Subject: [PATCH 09/13] handle missing profile for notices better in NoticeList --- lib/noticelist.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/noticelist.php b/lib/noticelist.php index e23cf3b6d5..17adf3a49c 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -96,8 +96,14 @@ class NoticeList extends Widget break; } - $item = $this->newListItem($this->notice); - $item->show(); + try { + $item = $this->newListItem($this->notice); + $item->show(); + } catch (Exception $e) { + // we log exceptions and continue + common_log(LOG_ERR, $e->getMessage()); + continue; + } } $this->out->elementEnd('ol'); From 1044f27e47faf89400c8a8734cdb9db21fad84bd Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 13 Jul 2010 10:59:15 -0400 Subject: [PATCH 10/13] handle missing profile for notices better in Rss10Action --- lib/rssaction.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/rssaction.php b/lib/rssaction.php index 62e3f21b61..f366db9729 100644 --- a/lib/rssaction.php +++ b/lib/rssaction.php @@ -178,7 +178,13 @@ class Rss10Action extends Action if (count($this->notices)) { foreach ($this->notices as $n) { - $this->showItem($n); + try { + $this->showItem($n); + } catch (Exception $e) { + // log exceptions and continue + common_log(LOG_ERR, $e->getMessage()); + continue; + } } } @@ -232,7 +238,7 @@ class Rss10Action extends Action function showItem($notice) { - $profile = Profile::staticGet($notice->profile_id); + $profile = $notice->getProfile(); $nurl = common_local_url('shownotice', array('notice' => $notice->id)); $creator_uri = common_profile_uri($profile); $this->elementStart('item', array('rdf:about' => $notice->uri, From 453a06fff4f92a9fd34db2899c012d828c69202e Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 13 Jul 2010 11:11:29 -0400 Subject: [PATCH 11/13] Exceptions are caught and continued on JSON, XML, Atom and RSS output in API --- lib/apiaction.php | 69 +++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/lib/apiaction.php b/lib/apiaction.php index 01985f0db3..297dcedece 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -734,14 +734,16 @@ class ApiAction extends Action 'xmlns:statusnet' => 'http://status.net/schema/api/1/')); if (is_array($notice)) { - foreach ($notice as $n) { - $twitter_status = $this->twitterStatusArray($n); - $this->showTwitterXmlStatus($twitter_status); - } - } else { - while ($notice->fetch()) { + $notice = new ArrayWrapper($notice); + } + + while ($notice->fetch()) { + try { $twitter_status = $this->twitterStatusArray($notice); $this->showTwitterXmlStatus($twitter_status); + } catch (Exception $e) { + common_log(LOG_ERR, $e->getMessage()); + continue; } } @@ -789,24 +791,16 @@ class ApiAction extends Action $this->element('ttl', null, '40'); if (is_array($notice)) { - foreach ($notice as $n) { - try { - $entry = $this->twitterRssEntryArray($n); - $this->showTwitterRssItem($entry); - } catch (Exception $e) { - common_log(LOG_ERR, $e->getMessage()); - // continue on exceptions - } - } - } else { - while ($notice->fetch()) { - try { - $entry = $this->twitterRssEntryArray($notice); - $this->showTwitterRssItem($entry); - } catch (Exception $e) { - common_log(LOG_ERR, $e->getMessage()); - // continue on exceptions - } + $notice = new ArrayWrapper($notice); + } + + while ($notice->fetch()) { + try { + $entry = $this->twitterRssEntryArray($notice); + $this->showTwitterRssItem($entry); + } catch (Exception $e) { + common_log(LOG_ERR, $e->getMessage()); + // continue on exceptions } } @@ -842,12 +836,15 @@ class ApiAction extends Action $this->element('subtitle', null, $subtitle); if (is_array($notice)) { - foreach ($notice as $n) { - $this->raw($n->asAtomEntry()); - } - } else { - while ($notice->fetch()) { + $notice = new ArrayWrapper($notice); + } + + while ($notice->fetch()) { + try { $this->raw($notice->asAtomEntry()); + } catch (Exception $e) { + common_log(LOG_ERR, $e->getMessage()); + continue; } } @@ -1042,14 +1039,16 @@ class ApiAction extends Action $statuses = array(); if (is_array($notice)) { - foreach ($notice as $n) { - $twitter_status = $this->twitterStatusArray($n); - array_push($statuses, $twitter_status); - } - } else { - while ($notice->fetch()) { + $notice = new ArrayWrapper($notice); + } + + while ($notice->fetch()) { + try { $twitter_status = $this->twitterStatusArray($notice); array_push($statuses, $twitter_status); + } catch (Exception $e) { + common_log(LOG_ERR, $e->getMessage()); + continue; } } From d73feb82d89d66593fd81f8bb5d10b1873fc9458 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 14 Jul 2010 10:38:34 -0400 Subject: [PATCH 12/13] cache sitemap notice and user counts for 4h --- classes/Memcached_DataObject.php | 4 ++-- plugins/Sitemap/Sitemap_notice_count.php | 4 +++- plugins/Sitemap/Sitemap_user_count.php | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 4579f64df8..a7fec365e0 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -593,7 +593,7 @@ class Memcached_DataObject extends Safe_DataObject return $c->get($cacheKey); } - static function cacheSet($keyPart, $value) + static function cacheSet($keyPart, $value, $flag=null, $expiry=null) { $c = self::memcache(); @@ -603,7 +603,7 @@ class Memcached_DataObject extends Safe_DataObject $cacheKey = common_cache_key($keyPart); - return $c->set($cacheKey, $value); + return $c->set($cacheKey, $value, $flag, $expiry); } static function valueString($v) diff --git a/plugins/Sitemap/Sitemap_notice_count.php b/plugins/Sitemap/Sitemap_notice_count.php index 2a375b3e48..6e0061e97b 100644 --- a/plugins/Sitemap/Sitemap_notice_count.php +++ b/plugins/Sitemap/Sitemap_notice_count.php @@ -153,7 +153,9 @@ class Sitemap_notice_count extends Memcached_DataObject $noticeCounts[$snc->notice_date] = $snc->notice_count; } - self::cacheSet('sitemap:notice:counts', $noticeCounts); + // Cache notice counts for 4 hours. + + self::cacheSet('sitemap:notice:counts', $noticeCounts, null, time() + 4 * 60 * 60); } return $noticeCounts; diff --git a/plugins/Sitemap/Sitemap_user_count.php b/plugins/Sitemap/Sitemap_user_count.php index 64b4c34428..98dd05bfed 100644 --- a/plugins/Sitemap/Sitemap_user_count.php +++ b/plugins/Sitemap/Sitemap_user_count.php @@ -154,7 +154,9 @@ class Sitemap_user_count extends Memcached_DataObject $userCounts[$suc->registration_date] = $suc->user_count; } - self::cacheSet('sitemap:user:counts', $userCounts); + // Cache user counts for 4 hours. + + self::cacheSet('sitemap:user:counts', $userCounts, null, time() + 4 * 60 * 60); } return $userCounts; From 65862d8f7f45d487d4714137af96d3a24e4ca386 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 16 Jul 2010 14:40:22 -0700 Subject: [PATCH 13/13] Suppress HTTP error headers for JSONP API output --- lib/apiaction.php | 20 +++++++++++++------- lib/apiauth.php | 38 +++----------------------------------- 2 files changed, 16 insertions(+), 42 deletions(-) diff --git a/lib/apiaction.php b/lib/apiaction.php index 297dcedece..7868ecab15 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -126,6 +126,7 @@ class ApiAction extends Action var $max_id = null; var $since_id = null; var $source = null; + var $callback = null; var $access = self::READ_ONLY; // read (default) or read-write @@ -145,6 +146,7 @@ class ApiAction extends Action parent::prepare($args); $this->format = $this->arg('format'); + $this->callback = $this->arg('callback'); $this->page = (int)$this->arg('page', 1); $this->count = (int)$this->arg('count', 20); $this->max_id = (int)$this->arg('max_id', 0); @@ -1185,9 +1187,8 @@ class ApiAction extends Action header('Content-Type: application/json; charset=utf-8'); // Check for JSONP callback - $callback = $this->arg('callback'); - if ($callback) { - print $callback . '('; + if (isset($this->callback)) { + print $this->callback . '('; } break; case 'rss': @@ -1216,8 +1217,7 @@ class ApiAction extends Action case 'json': // Check for JSONP callback - $callback = $this->arg('callback'); - if ($callback) { + if (isset($this->callback)) { print ')'; } break; @@ -1247,7 +1247,10 @@ class ApiAction extends Action $status_string = ClientErrorAction::$status[$code]; - header('HTTP/1.1 '.$code.' '.$status_string); + // Do not emit error header for JSONP + if (!isset($this->callback)) { + header('HTTP/1.1 '.$code.' '.$status_string); + } if ($format == 'xml') { $this->initDocument('xml'); @@ -1280,7 +1283,10 @@ class ApiAction extends Action $status_string = ServerErrorAction::$status[$code]; - header('HTTP/1.1 '.$code.' '.$status_string); + // Do not emit error header for JSONP + if (!isset($this->callback)) { + header('HTTP/1.1 '.$code.' '.$status_string); + } if ($content_type == 'xml') { $this->initDocument('xml'); diff --git a/lib/apiauth.php b/lib/apiauth.php index 91cb64262b..cf7a2692ca 100644 --- a/lib/apiauth.php +++ b/lib/apiauth.php @@ -227,7 +227,7 @@ class ApiAuthAction extends ApiAction } catch (OAuthException $e) { common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage()); - $this->showAuthError(); + $this->clientError($e->getMessage(), 401, $this->format); exit; } } @@ -265,7 +265,7 @@ class ApiAuthAction extends ApiAction // show error if the user clicks 'cancel' - $this->showAuthError(); + $this->clientError("Could not authenticate you.", 401, $this->format); exit; } else { @@ -298,7 +298,7 @@ class ApiAuthAction extends ApiAction $proxy, $ip); common_log(LOG_WARNING, $msg); - $this->showAuthError(); + $this->clientError("Could not authenticate you.", 401, $this->format); exit; } } @@ -345,36 +345,4 @@ class ApiAuthAction extends ApiAction } } } - - /** - * Output an authentication error message. Use XML or JSON if one - * of those formats is specified, otherwise output plain text - * - * @return void - */ - - function showAuthError() - { - header('HTTP/1.1 401 Unauthorized'); - $msg = 'Could not authenticate you.'; - - if ($this->format == 'xml') { - header('Content-Type: application/xml; charset=utf-8'); - $this->startXML(); - $this->elementStart('hash'); - $this->element('error', null, $msg); - $this->element('request', null, $_SERVER['REQUEST_URI']); - $this->elementEnd('hash'); - $this->endXML(); - } elseif ($this->format == 'json') { - header('Content-Type: application/json; charset=utf-8'); - $error_array = array('error' => $msg, - 'request' => $_SERVER['REQUEST_URI']); - print(json_encode($error_array)); - } else { - header('Content-type: text/plain'); - print "$msg\n"; - } - } - }