<?php
/**
* QueryString: Helps build a query string for use in links.
* Copyright (C) 2009 Dimitry Zolotaryov
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* The QueryString class is a helper for building complex query strings. Useful
* for the applications with many query parameters, the user can load a $_GET
* into an instance of QueryString, modify the key/values and append the string
* to a URL. The class can also be used in creating a POST request's body and
* specialized query string, like those found in CakePHP.
*
* Use:
* echo qs( array('key' => 'value') )->add('key2', 'value2');
* // key=value&key2=value2
*
* @author Dimitry Zolotaryov <hide@address.com>
*/
class QueryString {
/**
* List of keys and values
*
* @var array
*/
var $vars = array();
/**
* Values to keep
*
* @var array
*/
var $keep = array();
/**
* Options used in formating
*
* @var array
*/
var $options = array();
/**
* The constructor.
*
* Set the starting query string
*
* @param $vars
* @return QueryString
*/
function QueryString( $vars, $options = array() ) {
$options = array_merge( array(
'kvsep' => '=',
'pairsep' => '&',
'encode' => 'rawurlencode',
'subkey' => '%key[%sub]'
), $options);
$this->options = $options;
$this->vars = $vars;
}
/**
* Adds a key and value to the query string
*
* @param string $key The key
* @param mixed $value The value
* @return QueryString
*/
function add( $key, $value ) {
if ( isset( $this->vars[ $key ] )) {
if ( !is_array( $this->vars[ $key ] )) {
$this->vars[ $key ] = array( $this->vars[ $key ] );
}
$this->vars[ $key ][] = $value;
} else {
$this->set( $key, $value );
}
return $this;
}
/**
* Replaces the value of a key with $value
*
* @param string $key The key
* @param string $value The value
* @return QueryString
*/
function set( $key, $value ) {
$this->vars[ $key ] = $value;
return $this;
}
/**
* Removes a key from the query string
*
* @param string $key
* @return QueryString
*/
function rem( $key ) {
if ( isset( $this->vars[ $key ] )) {
unset( $this->vars[ $key ] );
}
return $this;
}
/**
* Keeps only these query keys when building the string
*
* @param string|array $keys The keys to keep
* @return QueryString
*/
function keep( $keys ) {
if ( is_array( $keys )) {
$this->keep = $keys;
} else {
$this->keep[] = $keys;
}
return $this;
}
/**
* Function returns a string representation of the query string
*
* @return string
*/
function __toString() {
$kept = array_keys( $this->vars );
if ( !empty( $this->keep )) {
$kept = array_intersect( $kept, $this->keep );
}
$kept = array_unique( $kept );
$qss = array();
foreach ( $kept as $key ) {
$value = $this->vars[ $key ];
$this->__appendToQS( $key, $value, $qss );
}
return join( $this->options['pairsep'], $qss );
}
/**
* Appends the key and value to the passed query string array
*
* @param string $key
* @param mixed $value
* @param array $querystring
*/
function __appendToQS( $key, $value, &$querystring ) {
if ( is_array( $value )) {
foreach ( $value as $k => $v ) {
$newkey = str_replace(
array('%key', '%sub'),
array( $key , $k ),
$this->options['subkey']
);
$this->__appendToQS( $newkey, $v, $querystring );
}
} else {
if ( $this->options['encode'] ) {
$key = $this->options['encode']( $key );
$value = $this->options['encode']( $value );
}
$querystring[] = $key . $this->options['kvsep'] . $value;
}
}
/**
* Returns the string for this QueryString
*
* @return string
*/
function string() {
return $this->__toString();
}
/**
* Function to test this class
*
*/
static function test() {
echo "Starting the test\n";
$qs = new QueryString(array('key' => 'value'));
assert( $qs->string() == 'key=value' );
$qs = new QueryString(array('key' => 'value'));
$qs->add('key2', 'value2');
assert( $qs->string() == 'key=value&key2=value2');
$qs = new QueryString(array('key' => 'value'));
$qs->add('key', 'value2');
assert( $qs->string() == 'key%5B0%5D=value&key%5B1%5D=value2');
$qs = new QueryString(array('key is' => 'value'));
assert( $qs->string() == 'key%20is=value');
$qs = new QueryString(array(
'cities' => array('Miami', 'Montreal'),
'people' => array(
'male' => array('John', 'Mike'),
'female' => array('Jane', 'Michelle')
)
));
assert( $qs->string() ==
'cities%5B0%5D=Miami&cities%5B1%5D=Montreal&'
. 'people%5Bmale%5D%5B0%5D=John&people%5Bmale%5D%5B1%5D=Mike&'
. 'people%5Bfemale%5D%5B0%5D=Jane&people%5Bfemale%5D%5B1%5D=Michelle'
);
$qs = qs( array('key' => 'value', 'key2' => 'value' ) )->keep('key2');
assert( $qs->string() == 'key2=value' );
// cake php
$qs = new CakeQueryString( array('key' => 'value', 'key2' => 'value2') );
echo "Test completed\n";
}
}
/**
* A QueryString builder for use in CakePHP
*
*/
class CakeQueryString extends QueryString {
/**
* Default constructor
*
* @param string $vars
* @param array $options
*/
function CakeQueryString( $vars, $options = array() ) {
$options = array_merge( array(
'kvsep' => ':',
'pairsep' => '/'
), $options );
return parent::QueryString( $vars, $options );
}
}
/**
* Function for building a quick query string
*
*/
if ( !function_exists('qs')) {
function qs( $params ) {
return new QueryString( $params );
}
}
// testing implementation
// > php querystring.class.php
if ( !empty( $argc ) && strstr( $argv[0], basename( __FILE__ )) ) {
function assert_failure( $file, $line, $code ) {
echo "Assertion failed (line $line): $code \n";
}
ini_set('error_reporting', E_ALL);
error_reporting( E_ALL );
assert_options( ASSERT_CALLBACK, 'assert_failure' );
QueryString::test();
}