From e1025d1d167fd91d7e8eefc621ffcc18fa3a6bf0 Mon Sep 17 00:00:00 2001 From: Bruce Alderson Date: Sun, 12 Aug 2012 15:03:36 -0700 Subject: [PATCH 001/221] Fixed delegate preflight (allows for empty pattern matches). Fixed path comparison to match more obviously (excluding class and extra cruft). --- lib/api.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/api.php b/lib/api.php index 5e51cc7..c8ad42d 100644 --- a/lib/api.php +++ b/lib/api.php @@ -65,9 +65,10 @@ public function add_delegate($regex, $delegateFn) { // check for conflicts in previously added delegates. if (array_key_exists($regex, $this->delegates)) throw new Exception("URI delegate already exists: pattern collision for '$regex'", 500); - + // preflight (and compile + cache) the regex ... errors handled by Presto. - if (!preg_match($regex, '')) $this->delegates[$regex] = $delegateFn; + preg_match($regex, ''); + $this->delegates[$regex] = $delegateFn; } /* Do delegation for hierarchical sub-routes @@ -80,7 +81,7 @@ public function delegate($ctx, $data = null) { if (empty($this->delegates) || empty($ctx) || empty($ctx->params)) throw new Exception('Unserviceable internal delegation attempt.', 501); - $path = implode('/', $ctx->params); + $path = implode('/', array_slice($ctx->params, 1)); foreach ($this->delegates as $p => $d) { if (preg_match($p, $path)) { if (empty($data)) return $this->$d($ctx); From ba770cab734ec25d42728fc9296d8dc45c8c51a4 Mon Sep 17 00:00:00 2001 From: Bruce Alderson Date: Sat, 8 Sep 2012 14:06:36 -0700 Subject: [PATCH 002/221] Added basic doc viewer as base info site. --- .gitmodules | 3 ++ .htaccess | 15 ++++++ docs/inc.php | 1 + docs/index.php | 107 +++++++++++++++++++++++++++++++++++++++++ docs/js/md.js | 0 docs/lib/markdown | 1 + docs/styles/presto.css | 0 7 files changed, 127 insertions(+) create mode 100644 .gitmodules create mode 100644 .htaccess create mode 120000 docs/inc.php create mode 100644 docs/index.php create mode 100644 docs/js/md.js create mode 160000 docs/lib/markdown create mode 100644 docs/styles/presto.css diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4a7af81 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "docs/lib/markdown"] + path = docs/lib/markdown + url = https://github.com/gavroche/php-markdown-extra.git diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..89f31b7 --- /dev/null +++ b/.htaccess @@ -0,0 +1,15 @@ + + +RewriteEngine On +RewriteBase /docs/ + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule ^(.*)$ index.php?presto&page=$1 [NC,L] + + + + +ErrorDocument 404 /docs/index.php?presto&error=404 +ErrorDocument 500 /docs/index.php?presto&error=500 +ErrorDocument 401 /docs/index.php?presto&error=401 +ErrorDocument 403 /docs/index.php?presto&error=403 \ No newline at end of file diff --git a/docs/inc.php b/docs/inc.php new file mode 120000 index 0000000..028bea4 --- /dev/null +++ b/docs/inc.php @@ -0,0 +1 @@ +../lib/inc.php \ No newline at end of file diff --git a/docs/index.php b/docs/index.php new file mode 100644 index 0000000..f9f9e39 --- /dev/null +++ b/docs/index.php @@ -0,0 +1,107 @@ + + + + + + + + + + + + + <?= $title ?> + + + + + + + + + + + +
+

+ + + +
+ +
+
+ + +

Not found

+

The document you were looking for wasn't found.

+ + + + +
+
+ + + + + + + + + + + + diff --git a/docs/js/md.js b/docs/js/md.js new file mode 100644 index 0000000..e69de29 diff --git a/docs/lib/markdown b/docs/lib/markdown new file mode 160000 index 0000000..5c4cf40 --- /dev/null +++ b/docs/lib/markdown @@ -0,0 +1 @@ +Subproject commit 5c4cf40fef1ded899e817c108c2b44e8d1014565 diff --git a/docs/styles/presto.css b/docs/styles/presto.css new file mode 100644 index 0000000..e69de29 From dfa39ef74b57aa9d10c2e301b4e6ffff94e7021a Mon Sep 17 00:00:00 2001 From: Bruce Alderson Date: Sat, 8 Sep 2012 14:07:01 -0700 Subject: [PATCH 003/221] Moved examples to docs. --- {examples => docs/examples}/.htaccess | 0 {examples => docs/examples}/delegator.php | 0 {examples => docs/examples}/index.php | 0 {examples => docs/examples}/info.php | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {examples => docs/examples}/.htaccess (100%) rename {examples => docs/examples}/delegator.php (100%) rename {examples => docs/examples}/index.php (100%) rename {examples => docs/examples}/info.php (100%) diff --git a/examples/.htaccess b/docs/examples/.htaccess similarity index 100% rename from examples/.htaccess rename to docs/examples/.htaccess diff --git a/examples/delegator.php b/docs/examples/delegator.php similarity index 100% rename from examples/delegator.php rename to docs/examples/delegator.php diff --git a/examples/index.php b/docs/examples/index.php similarity index 100% rename from examples/index.php rename to docs/examples/index.php diff --git a/examples/info.php b/docs/examples/info.php similarity index 100% rename from examples/info.php rename to docs/examples/info.php From e69dcc22ee812e55a6f749109a72a12dd8397808 Mon Sep 17 00:00:00 2001 From: Bruce Alderson Date: Sat, 8 Sep 2012 14:07:39 -0700 Subject: [PATCH 004/221] Renamed examples as setup tests (will add real examples back later, as a form of tutorials). --- docs/{examples => setup-tests}/.htaccess | 0 docs/{examples => setup-tests}/delegator.php | 0 docs/{examples => setup-tests}/index.php | 0 docs/{examples => setup-tests}/info.php | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename docs/{examples => setup-tests}/.htaccess (100%) rename docs/{examples => setup-tests}/delegator.php (100%) rename docs/{examples => setup-tests}/index.php (100%) rename docs/{examples => setup-tests}/info.php (100%) diff --git a/docs/examples/.htaccess b/docs/setup-tests/.htaccess similarity index 100% rename from docs/examples/.htaccess rename to docs/setup-tests/.htaccess diff --git a/docs/examples/delegator.php b/docs/setup-tests/delegator.php similarity index 100% rename from docs/examples/delegator.php rename to docs/setup-tests/delegator.php diff --git a/docs/examples/index.php b/docs/setup-tests/index.php similarity index 100% rename from docs/examples/index.php rename to docs/setup-tests/index.php diff --git a/docs/examples/info.php b/docs/setup-tests/info.php similarity index 100% rename from docs/examples/info.php rename to docs/setup-tests/info.php From 84e0d05e89ec68c50965120bde823ed0347f3e38 Mon Sep 17 00:00:00 2001 From: Bruce Alderson Date: Sat, 8 Sep 2012 14:10:19 -0700 Subject: [PATCH 005/221] Added github link + favicon files. --- docs/favicon.ico | 0 docs/favicon.png | 0 docs/index.php | 2 ++ 3 files changed, 2 insertions(+) create mode 100644 docs/favicon.ico create mode 100644 docs/favicon.png diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/docs/favicon.png b/docs/favicon.png new file mode 100644 index 0000000..e69de29 diff --git a/docs/index.php b/docs/index.php index f9f9e39..3d10745 100644 --- a/docs/index.php +++ b/docs/index.php @@ -42,6 +42,8 @@ function g($k, $d = null) { return array_key_exists($k, $_GET) ? $_GET[$k] : $d; +Fork me on GitHub +

From e57684093bde1d2cd4357f92ea122d79d5a2d588 Mon Sep 17 00:00:00 2001 From: Bruce Alderson Date: Sat, 8 Sep 2012 14:20:51 -0700 Subject: [PATCH 006/221] Added basic fonts and styles. Upgraded to less. Experience points += 10. --- docs/index.php | 4 ++-- docs/styles/presto.css | 2 ++ docs/styles/presto.less | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 docs/styles/presto.less diff --git a/docs/index.php b/docs/index.php index 3d10745..c611186 100644 --- a/docs/index.php +++ b/docs/index.php @@ -24,7 +24,7 @@ function g($k, $d = null) { return array_key_exists($k, $_GET) ? $_GET[$k] : $d; - + <?= $title ?> @@ -42,7 +42,7 @@ function g($k, $d = null) { return array_key_exists($k, $_GET) ? $_GET[$k] : $d; -Fork me on GitHub +Fork me on GitHub

diff --git a/docs/styles/presto.css b/docs/styles/presto.css index e69de29..e0f1a16 100644 --- a/docs/styles/presto.css +++ b/docs/styles/presto.css @@ -0,0 +1,2 @@ +body{font:105%/150% "freight-sans-pro","Helvetica Neau",Helvetica;}body header,body section,body footer{width:100%;position:relative;} +body header>div,body section>div,body footer>div{width:40em;margin:0 auto;} diff --git a/docs/styles/presto.less b/docs/styles/presto.less new file mode 100644 index 0000000..559e508 --- /dev/null +++ b/docs/styles/presto.less @@ -0,0 +1,14 @@ + +body { + font: 105%/150% "freight-sans-pro", "Helvetica Neau", Helvetica; + + header, section, footer { + width: 100%; + position: relative; + } + header>div, section>div, footer>div { + width: 40em; + margin: 0 auto; + } + +} \ No newline at end of file From 3a67c11e0b205586d0d56a9367964564a066a5e9 Mon Sep 17 00:00:00 2001 From: Bruce Alderson Date: Sat, 8 Sep 2012 17:09:47 -0700 Subject: [PATCH 007/221] Updated README and basic styles. --- README.md | 111 +++++++++++++++++----------------------- docs/index.php | 4 +- docs/styles/presto.css | 8 +++ docs/styles/presto.less | 54 +++++++++++++++++++ 4 files changed, 112 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 1e87650..07d4727 100644 --- a/README.md +++ b/README.md @@ -1,86 +1,71 @@ -Presto - PHP REST toolkit -========================= +Presto is a toolkit for building RESTful APIs in PHP 5. It's lightweight, decoupled, and focused on making web apps the right way; using APIs and clean URLs to produce output in standard formats like JSON, HTML, and XML. It encourages separating views completely from your model and controller code, and to animate your user interfaces in HTML, CSS, and JavaScript. It also uses existing tools and libraries for what they're good at, like letting Apache deal with routing, and letting PHP load and execute a mininimal amount of dynamic code. -Presto is a small library for building simple, RESTful APIs using PHP. It's lightweight, decoupled, and focused on making web apps the right way; using APIs with clean URLs, which produce simple, clean output in standard formats like JSON, HTML, and XML. +How is Presto different? +======================== -How is it different? --------------------- +Presto does away with standard MVC. It focuses on APIs built from simple classes, relying on the web server for routing and on PHP autoloading for delegating requests to class member calls. User interfaces are left to static HTML views, animated with JavaScript and CSS. -Presto has no views, no models, and no controllers[^1]. Instead it focuses on APIs built from simple classes. It encourages applications to be based on APIs, which feed web applications animated with HTML/CSS/JavaScript. +### A quick example -A Presto API is simply a PHP class that maps to a resource or tree of resources. It has: +An API is a class with members named for each resource (or tree of resources). For example, an `apple` is a resource. You can request an `apple` over HTTP: -* Public members that map to requests (`GET thing/details.json` would map to `$thing->get_details()`) -* Request parameters and input payloads that are packaged up and sanitized -* Each route returns data as DOMs, which are automatically adapted to the requested `ContentType` -* Errors are automatically converted into returned HTTP statuses + GET apples/spartan.json&large+red -The resulting code is focused on resources and the rules for those resources, and not boilerplate, excessive error checking, routing, and output generation. +Presto maps the request to a file, class, and class member automatically. It loads the file, creates an instance of the class, and executes the member that best fits: -[^1]: Note that Presto avoids MVC by solving a smaller problem and relying on built in functionality for delegation and generating output. + /* Loaded from 'apples.php' */ + + class apples extends API { + + public function get($ctx) { + + $thing = array( + 'name' => @$ctx->params[0], + 'size' => @$ctx->params[1] + // etc + ); + + return $thing; + } + } + +The `$thing` is automatically converted by Presto to the requested `Content-Type`, either implied by the request or the appropriate HTTP header. For formats not supported by default, *Output Adapters* can be defined and registered, or the type can be passed through for resources that are not based DOM style data. -How it works ------------- +Any HTTP request type is mapped automatically. For example, you can request a list of `apple` types available from the API: -Presto uses the best parts of PHP and Apache, as they're intended to be used. It relies on `.htaccess` for routing, and built-in PHP behaviours for delegation and class loading. The principle is that relying on existing, simple approaches results in less framework, fewer bugs, and less to learn that isn't useful elsewhere. + LIST apples.json&red -*Note: PHP 5.3 or better is requried, as Presto relies on anonymous functions, and other newer PHP features. This makes it possible to write very clean, simple web services.* +A LIST request is mapped to a function of the same name: -An example API --------------- + public function list($ctx) { return array(); } - class info - extends API { - - // Set up the API - public function __construct() { - parent::__construct(get_class()); - if (!self::isSignedIn()) throw new Exception('Auth required', 401); - } - - // GET info/time.json - gets the local machine time - public function get_time($ctx) { - $dom = (object) getdate(); - return $dom; // <-- returns the DOM as JSON - } +More complex resources are possible by either delegating (based on regex patterns), or by adding specific handlers. For example, you could add a `seeds` branch to the `apple` resource. Getting a list of seeds would map to: + + public function list_seeds($ctx) { array(); } + +### What about errors? + +Errors are handled by the toolkit as standard PHP exceptions and standard PHP errors are remapped (where possible) to exceptions to ensure that logic and code errors are returned to clients as valid API returns. This means that individual APIs do not need to check return values, nor do they need to `try` and `catch` unless they need to do something special. Presto maps the standard exceptions to HTTP statuses and output in whatever format was requested where possible. + +For example, if you encounter a parameter error you can simply throw an exception: + + if (empty($param)) + throw new Exception("Missing required parameter", 400); - // PUT info/time.json - public function put_time($ctx) { throw new Exception('Not implemented', 501); } - // POST info/time.json - public function post_time($ctx) { throw new Exception('Not implemented', 501); } - } +Presto translates the exception into a `400` with an appropriately encoded body. -The class promises a GET, PUT, and POST interface for time. All other requests will return a standard 404. The PUT and POST are not implemented yet, so they return a 501 (not implemented). +The resulting API code is much more focused on carefully testing parameters, retrieving appropriate resources, and building rich DOMs instead of boilerplate code, managing responses, excessive error checking, routing, and other complex output generation. -Presto maps requests to PHP files and objects, providing parameters and other calling information in the `$ctx` parameter. A request like: - GET /info/time.json - -Maps to: - [info.php] info->get_time($ctx); - -The context includes the call parameters, the `content-type`, and request method. Handling of `content-type` is built in for simple cases, so that any data returned from an API is automatically transformed into the required type. For our simple example, returning the result of `datetime()` to the request produces: - - { - "seconds" => 40 - "minutes" => 58 - "hours" => 21 - "mday" => 17 - "wday" => 2 - "mon" => 6 - "year" => 2003 - "yday" => 167 - "weekday" => "Tuesday" - "month" => "June" - "0" => 1055901520 - } -Advanced features ------------------ +Other interesting features +========================== -There are a number of ways you can extend Presto. You can: +You can also: * Add additional `content-types` by adding *output adapters* (`JSON`, simple `XML`, and simple `HTML` are built in) * Add `content-type` filters to define what types of payloads a given resource supports. * Add *custom delegation* for special resource types + +**Note that PHP 5.3 or better is requried, as Presto relies on anonymous functions and other newer PHP features. This makes it possible to write very clean, simple web services.** diff --git a/docs/index.php b/docs/index.php index c611186..042355f 100644 --- a/docs/index.php +++ b/docs/index.php @@ -8,7 +8,7 @@ function g($k, $d = null) { return array_key_exists($k, $_GET) ? $_GET[$k] : $d; $page = g('page', ''); $name = 'pRESTo'; - $tagline = 'PHP + REST'; + $tagline = 'Simpler REST with PHP'; $title = $page ? "$name - $page" : "$name - $tagline"; if (empty($page)) $page = 'README.md'; @@ -45,7 +45,7 @@ function g($k, $d = null) { return array_key_exists($k, $_GET) ? $_GET[$k] : $d; Fork me on GitHub
-

+

+ diff --git a/docs/setup-tests/info.php b/setup-tests/info.php similarity index 100% rename from docs/setup-tests/info.php rename to setup-tests/info.php From 2aa765c1a1cee71d458b0c18c80d3b0377463003 Mon Sep 17 00:00:00 2001 From: Bruce Alderson Date: Sun, 9 Sep 2012 12:33:35 -0700 Subject: [PATCH 011/221] Fixed typo --- setup-tests/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup-tests/index.php b/setup-tests/index.php index 47a07fb..276d5f6 100644 --- a/setup-tests/index.php +++ b/setup-tests/index.php @@ -30,7 +30,7 @@ Fork me on GitHub
-

Presto install checklist

+

Presto Install checklist

From ff9d937b277347ef0b827a90b55bcdc8d7c71fc3 Mon Sep 17 00:00:00 2001 From: Bruce Alderson Date: Sun, 9 Sep 2012 13:48:56 -0700 Subject: [PATCH 012/221] Updated docs, adding nav links, hooking in dev docs, and improving code highlighting. --- README.md | 10 +++++++--- docs/application-state.md | 2 ++ docs/configuration.md | 2 ++ docs/faq.md | 4 +--- docs/generating-output.md | 2 +- docs/index.md | 18 ++++++++++++++---- docs/index.php | 6 +++++- docs/nav.php | 5 +++++ docs/styles/presto.css | 2 +- docs/styles/presto.less | 7 +++++-- setup-tests/index.php | 15 ++++++++------- 11 files changed, 51 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 07d4727..076fb7e 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,10 @@ An API is a class with members named for each resource (or tree of resources). F GET apples/spartan.json&large+red + Presto maps the request to a file, class, and class member automatically. It loads the file, creates an instance of the class, and executes the member that best fits: + /* Loaded from 'apples.php' */ class apples extends API { @@ -28,7 +30,8 @@ Presto maps the request to a file, class, and class member automatically. It loa return $thing; } } - + + The `$thing` is automatically converted by Presto to the requested `Content-Type`, either implied by the request or the appropriate HTTP header. For formats not supported by default, *Output Adapters* can be defined and registered, or the type can be passed through for resources that are not based DOM style data. Any HTTP request type is mapped automatically. For example, you can request a list of `apple` types available from the API: @@ -39,6 +42,7 @@ A LIST request is mapped to a function of the same name: public function list($ctx) { return array(); } + More complex resources are possible by either delegating (based on regex patterns), or by adding specific handlers. For example, you could add a `seeds` branch to the `apple` resource. Getting a list of seeds would map to: public function list_seeds($ctx) { array(); } @@ -49,8 +53,8 @@ Errors are handled by the toolkit as standard PHP exceptions and standard PHP er For example, if you encounter a parameter error you can simply throw an exception: - if (empty($param)) - throw new Exception("Missing required parameter", 400); +if (empty($param)) + throw new Exception("Missing required parameter", 400); Presto translates the exception into a `400` with an appropriately encoded body. diff --git a/docs/application-state.md b/docs/application-state.md index e69de29..be735af 100644 --- a/docs/application-state.md +++ b/docs/application-state.md @@ -0,0 +1,2 @@ +Application state (AKA, sessions) +--------------------------------- \ No newline at end of file diff --git a/docs/configuration.md b/docs/configuration.md index e69de29..b26e07e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -0,0 +1,2 @@ +Configuration +------------- \ No newline at end of file diff --git a/docs/faq.md b/docs/faq.md index 2a40c26..313aa55 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,7 +1,5 @@ Frequently asked things -======================= - -Document style: FAQ +----------------------- ## Q: Why no MVC? diff --git a/docs/generating-output.md b/docs/generating-output.md index 7b3becf..0c660bb 100644 --- a/docs/generating-output.md +++ b/docs/generating-output.md @@ -1,5 +1,5 @@ Generating output -================== +----------------- All Presto API functions return a simple DOM object. This object (or array) is translated into output using a type handler, which is selected by the requested `content-type`. diff --git a/docs/index.md b/docs/index.md index c0cb9db..d522f7c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,9 +1,19 @@ -Presto for PHP - developer documentation -======================================== +Developer documentation +----------------------- +[Frequently Asked Things (FAQ)](/docs/faq.html) -Finding your way around ------------------------ +[Application state](/docs/application-state.html) + +[Configuring Presto](/docs/configuration.html) + +[Output adapters](/docs/generating-output.html) + + +Future topics +============= + +These are things that aren't yet covered in the documentation. * API classes (how requests are mapped to members) * Returning data to clients diff --git a/docs/index.php b/docs/index.php index 86cc03d..df3447f 100644 --- a/docs/index.php +++ b/docs/index.php @@ -12,6 +12,7 @@ function g($k, $d = null) { return array_key_exists($k, $_GET) ? $_GET[$k] : $d; $title = $page ? "$name - $page" : "$name - $tagline"; if (empty($page)) $page = 'README.md'; + else $page = str_replace('.html', '.md', $page); $file = realpath(API_BASE."/$page"); @@ -63,7 +64,8 @@ function g($k, $d = null) { return array_key_exists($k, $_GET) ? $_GET[$k] : $d;

The document you were looking for wasn't found.

', '
', $text);
 ?>
 
 
 
+
+
 
 
+
 
 
 

From 188a66edf8bef25f5ebf7262dae53789d9518356 Mon Sep 17 00:00:00 2001
From: Bruce Alderson 
Date: Sun, 9 Sep 2012 21:08:09 -0700
Subject: [PATCH 013/221] Polished styles.

---
 README.md               |   4 ++--
 docs/favicon.png        | Bin 0 -> 140 bytes
 docs/index.php          |   2 +-
 docs/styles/presto.css  |   7 +++++--
 docs/styles/presto.less |  29 ++++++++++++++++++++++++++---
 setup-tests/index.php   |  12 ++++++------
 6 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/README.md b/README.md
index 076fb7e..12e5385 100644
--- a/README.md
+++ b/README.md
@@ -53,8 +53,8 @@ Errors are handled by the toolkit as standard PHP exceptions and standard PHP er
 
 For example, if you encounter a parameter error you can simply throw an exception:
 
-if (empty($param))
-	throw new Exception("Missing required parameter", 400);
+	if (empty($param))
+		throw new Exception("Missing required parameter", 400);
 		
 Presto translates the exception into a `400` with an appropriately encoded body.
 
diff --git a/docs/favicon.png b/docs/favicon.png
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2cd5843ad31222b37ed6f5bacbaeade73ab2390b 100644
GIT binary patch
literal 140
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`!JaOTAr-gIPTtMQpuod=_tefi
zmo$xBe|0qRy969M>|f}5V7Jqxgfw13L;kzXYgWk#%uLBokvyRpwvJWd&9mahmx>au
oRv-TH+udfan))(%(fOSWC*xbry>8Ji02FVdQ&MBb@0PPksP5=M^

literal 0
HcmV?d00001

diff --git a/docs/index.php b/docs/index.php
index df3447f..0026430 100644
--- a/docs/index.php
+++ b/docs/index.php
@@ -76,7 +76,7 @@ function g($k, $d = null) { return array_key_exists($k, $_GET) ? $_GET[$k] : $d;