From cccc2d1161edc231fd536bea8c7655d09a0cc7c2 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Thu, 28 Jun 2018 12:29:50 -0400 Subject: [PATCH] Add lazy-loading option to WireInput, specified by $config->wireInputLazy=true; --- wire/config.php | 16 ++++++- wire/core/Config.php | 1 + wire/core/ProcessWire.php | 3 +- wire/core/WireInput.php | 63 +++++++++++++++++++++++-- wire/core/WireInputData.php | 91 +++++++++++++++++++++++++++++++++---- 5 files changed, 157 insertions(+), 17 deletions(-) diff --git a/wire/config.php b/wire/config.php index 8095279a..02b757c3 100644 --- a/wire/config.php +++ b/wire/config.php @@ -831,7 +831,21 @@ $config->maxPageNum = 999; */ $config->wireInputOrder = 'get post'; - +/** + * Lazy-load get/post/cookie input into $input API var? + * + * This is an experimental option for reduced memory usage when a lot of input data is present. + * + * This prevents PW from keeping separate copies of get/post/cookie data, and it instead works + * directly from the PHP $_GET, $_POST and $_COOKIE vars. + * + * This option is also useful in that anything you SET to PW’s $input->get/post/cookie also gets + * set to the equivalent PHP $_GET, $_POST and $_COOKIE. + * + * @var bool + * + */ +$config->wireInputLazy = false; /*** 7. DATABASE ********************************************************************************/ diff --git a/wire/core/Config.php b/wire/core/Config.php index 5d2203a6..59f05e87 100644 --- a/wire/core/Config.php +++ b/wire/core/Config.php @@ -83,6 +83,7 @@ * @property int $maxUrlSegmentLength Maximum length of any individual URL segment (default=128). #pw-group-URLs * @property int $maxUrlDepth Maximum URL/path slashes (depth) for request URLs. (Min=10, Max=60) #pw-group-URLs * @property string $wireInputOrder Order that variables with the $input API var are handled when you access $input->var. #pw-group-HTTP-and-input + * @property bool $wireInputLazy Specify true for $input API var to load input data in a lazy fashion and potentially use less memory. Default is false. #pw-group-HTTP-and-input * * @property bool $advanced Special mode for ProcessWire system development. Not recommended for regular site development or production use. #pw-group-system * @property bool $demo Special mode for demonstration use that causes POST requests to be disabled. Applies to core, but may not be safe with 3rd party modules. #pw-group-system diff --git a/wire/core/ProcessWire.php b/wire/core/ProcessWire.php index b81674b6..67cdd8a0 100644 --- a/wire/core/ProcessWire.php +++ b/wire/core/ProcessWire.php @@ -447,7 +447,8 @@ class ProcessWire extends Wire { $session = $this->wire('session', new Session($this), true); $this->initVar('session', $session); $this->wire('user', $users->getCurrentUser()); - $this->wire('input', new WireInput(), true); + $input = $this->wire('input', new WireInput(), true); + if($config->wireInputLazy) $input->setLazy(true); // populate admin URL before modules init() $config->urls->admin = $config->urls->root . ltrim($pages->getPath($config->adminRootPageID), '/'); diff --git a/wire/core/WireInput.php b/wire/core/WireInput.php index 78b23f5d..d1d515e5 100644 --- a/wire/core/WireInput.php +++ b/wire/core/WireInput.php @@ -74,6 +74,14 @@ class WireInput extends Wire { */ protected $pageNum = 1; + /** + * Use lazy loading method for get/post/cookie? + * + * @var bool + * + */ + protected $lazy = false; + /** * @var array * @@ -95,6 +103,18 @@ class WireInput extends Wire { $this->useFuel(false); $this->unregisterGLOBALS(); } + + /** + * Set for lazy loading + * + * Must be called before accessing any get/post/cookie input + * + * @param bool $lazy + * + */ + public function setLazy($lazy = true) { + $this->lazy = (bool) $lazy; + } /** * Retrieve a named GET variable value, or all GET variables (from URL query string) @@ -121,7 +141,7 @@ class WireInput extends Wire { */ public function get($key = '') { if(is_null($this->getVars)) { - $this->getVars = $this->wire(new WireInputData($_GET)); + $this->getVars = $this->wire(new WireInputData($_GET, $this->lazy)); $this->getVars->offsetUnset('it'); } return $key ? $this->getVars->__get($key) : $this->getVars; @@ -150,7 +170,7 @@ class WireInput extends Wire { * */ public function post($key = '') { - if(is_null($this->postVars)) $this->postVars = $this->wire(new WireInputData($_POST)); + if(is_null($this->postVars)) $this->postVars = $this->wire(new WireInputData($_POST, $this->lazy)); return $key ? $this->postVars->__get($key) : $this->postVars; } @@ -166,7 +186,7 @@ class WireInput extends Wire { * */ public function cookie($key = '') { - if(is_null($this->cookieVars)) $this->cookieVars = $this->wire(new WireInputData($_COOKIE)); + if(is_null($this->cookieVars)) $this->cookieVars = $this->wire(new WireInputData($_COOKIE, $this->lazy)); return $key ? $this->cookieVars->__get($key) : $this->cookieVars; } @@ -604,6 +624,20 @@ class WireInput extends Wire { return $this->httpHostUrl() . $this->url($withQueryString); } + /** + * Same as httpUrl() method but always uses https scheme, rather than current request scheme + * + * See httpUrl() method for argument and usage details. + * + * @param bool $withQueryString + * @return string + * @see WireInput::httpUrl() + * + */ + public function httpsUrl($withQueryString = false) { + return $this->httpHostUrl(true) . $this->url($withQueryString); + } + /** * Get current scheme and URL for hostname without any path or query string * @@ -611,11 +645,30 @@ class WireInput extends Wire { * * #pw-group-URLs * + * @param string|bool|null Optionally specify this argument to force a particular scheme (rather than using current): + * - boolean true to force “https” + * - boolean false to force “http” + * - string with scheme you want to use + * - blank string or "//" for no scheme, i.e. URL begins with "//" which refers to current scheme. + * - omit argument or null to use current request scheme (default behavior). * @return string * */ - public function httpHostUrl() { - return $this->scheme() . '://' . $this->wire('config')->httpHost; + public function httpHostUrl($scheme = null) { + if($scheme === true) { + $scheme = 'https://'; + } else if($scheme === false) { + $scheme = 'http://'; + } else if(is_string($scheme)) { + if(strlen($scheme)) { + if(strpos($scheme, '//') === false) $scheme = "$scheme://"; + } else { + $scheme = '//'; + } + } else { + $scheme = $this->scheme() . '://'; + } + return $scheme . $this->wire('config')->httpHost; } /** diff --git a/wire/core/WireInputData.php b/wire/core/WireInputData.php index 338c5850..3135a76e 100644 --- a/wire/core/WireInputData.php +++ b/wire/core/WireInputData.php @@ -16,7 +16,7 @@ * * Each WireInputData is not instantiated unless specifically asked for. * - * ProcessWire 3.x, Copyright 2016 by Ryan Cramer + * ProcessWire 3.x, Copyright 2018 by Ryan Cramer * https://processwire.com * * @link http://processwire.com/api/ref/input/ Offical $input API variable documentation @@ -70,16 +70,40 @@ class WireInputData extends Wire implements \ArrayAccess, \IteratorAggregate, \C */ protected $data = array(); + /** + * Are we working with lazy data (data by reference)? + * + * @var bool + * + */ + protected $lazy = false; + + /** + * When lazy mode is active, these are keys of values set in a non-lazy way + * + * @var array + * + */ + protected $unlazyKeys = array(); + /** * Construct * * @param array $input Associative array of variables to store + * @param bool $lazy Use lazy loading? * */ - public function __construct(array $input = array()) { + public function __construct(&$input = array(), $lazy = false) { $this->useFuel(false); $this->stripSlashes = get_magic_quotes_gpc(); - $this->setArray($input); + if(!empty($input)) { + if($lazy) { + $this->data = &$input; + $this->lazy = true; + } else { + $this->setArray($input); + } + } } /** @@ -101,7 +125,19 @@ class WireInputData extends Wire implements \ArrayAccess, \IteratorAggregate, \C * */ public function getArray() { - return $this->data; + if($this->lazy) { + $data = array(); + foreach($this->data as $key => $value) { + if(isset($this->unlazyKeys[$key])) { + $data[$key] = $value; + } else { + $data[$key] = $this->__get($key); + } + } + return $data; + } else { + return $this->data; + } } /** @@ -112,9 +148,13 @@ class WireInputData extends Wire implements \ArrayAccess, \IteratorAggregate, \C * */ public function __set($key, $value) { - if(is_string($value) && $this->stripSlashes) $value = stripslashes($value); - if(is_array($value)) $value = $this->cleanArray($value); + if(is_string($value)) { + if($this->stripSlashes) $value = stripslashes($value); + } else if(is_array($value)) { + $value = $this->cleanArray($value); + } $this->data[$key] = $value; + if($this->lazy) $this->unlazyKeys[$key] = $key; } /** @@ -154,12 +194,40 @@ class WireInputData extends Wire implements \ArrayAccess, \IteratorAggregate, \C * */ public function __get($key) { - // if($key == 'whitelist') return $this->whitelist; - return isset($this->data[$key]) ? $this->data[$key] : null; + + if(strpos($key, '|')) { + $value = null; + foreach(explode('|', $key) as $k) { + $value = $this->__get($k); + if($value !== null) break; + } + return $value; + + } else if(isset($this->data[$key])) { + $value = $this->data[$key]; + if($this->lazy && !isset($this->unlazyKeys[$key])) { + // in lazy mode, value is not cleaned until it is accessed + if(is_string($value)) { + if($this->stripSlashes) $value = stripslashes($value); + } else if(is_array($value)) { + $value = $this->cleanArray($value); + } + } + + } else { + $value = null; + } + + return $value; } public function getIterator() { - return new \ArrayObject($this->data); + if($this->lazy) { + $data = $this->getArray(); + return new \ArrayObject($data); + } else { + return new \ArrayObject($this->data); + } } public function offsetExists($key) { @@ -176,6 +244,7 @@ class WireInputData extends Wire implements \ArrayAccess, \IteratorAggregate, \C public function offsetUnset($key) { unset($this->data[$key]); + if($this->lazy && isset($this->unlazyKeys[$key])) unset($this->unlazyKeys[$key]); } public function count() { @@ -183,12 +252,14 @@ class WireInputData extends Wire implements \ArrayAccess, \IteratorAggregate, \C } public function remove($key) { - unset($this->data[$key]); + $this->offsetUnset($key); return $this; } public function removeAll() { $this->data = array(); + $this->lazy = false; + $this->unlazyKeys = array(); return $this; }