diff options
Diffstat (limited to 'engine/classes')
32 files changed, 898 insertions, 253 deletions
diff --git a/engine/classes/ElggAccess.php b/engine/classes/ElggAccess.php index 6f8d9bb4b..0aed477fc 100644 --- a/engine/classes/ElggAccess.php +++ b/engine/classes/ElggAccess.php @@ -16,6 +16,7 @@ class ElggAccess {  	 */  	private $ignore_access; +	// @codingStandardsIgnoreStart  	/**  	 * Get current ignore access setting.  	 * @@ -26,6 +27,7 @@ class ElggAccess {  		elgg_deprecated_notice('ElggAccess::get_ignore_access() is deprecated by ElggAccess::getIgnoreAccess()', 1.8);  		return $this->getIgnoreAccess();  	} +	// @codingStandardsIgnoreEnd  	/**  	 * Get current ignore access setting. @@ -36,6 +38,7 @@ class ElggAccess {  		return $this->ignore_access;  	} +	// @codingStandardsIgnoreStart  	/**  	 * Set ignore access.  	 * @@ -49,6 +52,7 @@ class ElggAccess {  		elgg_deprecated_notice('ElggAccess::set_ignore_access() is deprecated by ElggAccess::setIgnoreAccess()', 1.8);  		return $this->setIgnoreAccess($ignore);  	} +	// @codingStandardsIgnoreEnd  	/**  	 * Set ignore access. diff --git a/engine/classes/ElggAnnotation.php b/engine/classes/ElggAnnotation.php index 511b5151f..175e7049d 100644 --- a/engine/classes/ElggAnnotation.php +++ b/engine/classes/ElggAnnotation.php @@ -11,6 +11,9 @@   * @package    Elgg.Core   * @subpackage DataModel.Annotations   * @link       http://docs.elgg.org/DataModel/Annotations + * + * @property string $value_type + * @property string $enabled   */  class ElggAnnotation extends ElggExtender { @@ -56,6 +59,8 @@ class ElggAnnotation extends ElggExtender {  	 * Save this instance  	 *  	 * @return int an object id +	 * +	 * @throws IOException  	 */  	function save() {  		if ($this->id > 0) { diff --git a/engine/classes/ElggAttributeLoader.php b/engine/classes/ElggAttributeLoader.php index 602bb8bae..ffc80b02d 100644 --- a/engine/classes/ElggAttributeLoader.php +++ b/engine/classes/ElggAttributeLoader.php @@ -4,6 +4,9 @@   * Loads ElggEntity attributes from DB or validates those passed in via constructor   *   * @access private + * + * @package    Elgg.Core + * @subpackage DataModel   */  class ElggAttributeLoader { @@ -21,7 +24,7 @@ class ElggAttributeLoader {  		'time_created',  		'time_updated',  		'last_action', -		'enabled' +		'enabled',  	);  	/** @@ -65,9 +68,11 @@ class ElggAttributeLoader {  	public $full_loader = '';  	/** -	 * @param string $class class of object being loaded -	 * @param string $required_type entity type this is being used to populate -	 * @param array $initialized_attrs attributes after initializeAttributes() has been run +	 * Constructor +	 * +	 * @param string $class             class of object being loaded +	 * @param string $required_type     entity type this is being used to populate +	 * @param array  $initialized_attrs attributes after initializeAttributes() has been run  	 * @throws InvalidArgumentException  	 */  	public function __construct($class, $required_type, array $initialized_attrs) { @@ -87,14 +92,33 @@ class ElggAttributeLoader {  		$this->secondary_attr_names = array_diff($all_attr_names, self::$primary_attr_names);  	} +	/** +	 * Get primary attributes missing that are missing +	 * +	 * @param stdClass $row Database row +	 * @return array +	 */  	protected function isMissingPrimaries($row) {  		return array_diff(self::$primary_attr_names, array_keys($row)) !== array();  	} +	/** +	 * Get secondary attributes that are missing +	 * +	 * @param stdClass $row Database row +	 * @return array +	 */  	protected function isMissingSecondaries($row) {  		return array_diff($this->secondary_attr_names, array_keys($row)) !== array();  	} +	/** +	 * Check that the type is correct +	 * +	 * @param stdClass $row Database row +	 * @return void +	 * @throws InvalidClassException +	 */  	protected function checkType($row) {  		if ($row['type'] !== $this->required_type) {  			$msg = elgg_echo('InvalidClassException:NotValidElggStar', array($row['guid'], $this->class)); @@ -148,11 +172,11 @@ class ElggAttributeLoader {  				if (!is_callable($this->primary_loader)) {  					throw new LogicException('Primary attribute loader must be callable');  				} -				if (!$this->requires_access_control) { +				if ($this->requires_access_control) { +					$fetched = (array) call_user_func($this->primary_loader, $row['guid']); +				} else {  					$ignoring_access = elgg_set_ignore_access(); -				} -				$fetched = (array) call_user_func($this->primary_loader, $row['guid']); -				if (!$this->requires_access_control) { +					$fetched = (array) call_user_func($this->primary_loader, $row['guid']);  					elgg_set_ignore_access($ignoring_access);  				}  				if (!$fetched) { @@ -176,6 +200,8 @@ class ElggAttributeLoader {  						// saved, these are stored w/ type "site", but with no sites_entity row. These  						// are probably only created in the unit tests.  						// @todo Don't save vanilla ElggEntities with type "site" + +						$row = $this->filterAddedColumns($row);  						$row['guid'] = (int) $row['guid'];  						return $row;  					} @@ -185,15 +211,38 @@ class ElggAttributeLoader {  			}  		} -		// loading complete: re-check missing and check type -		if (($was_missing_primaries && $this->isMissingPrimaries($row)) -				|| ($was_missing_secondaries && $this->isMissingSecondaries($row))) { -			throw new LogicException('Attribute loaders failed to return proper attributes'); -		} +		$row = $this->filterAddedColumns($row); + +		// Note: If there are still missing attributes, we're running on a 1.7 or earlier schema. We let +		// this pass so the upgrades can run. -		// guid needs to be an int  http://trac.elgg.org/ticket/4111 +		// guid needs to be an int  https://github.com/elgg/elgg/issues/4111  		$row['guid'] = (int) $row['guid'];  		return $row;  	} + +	/** +	 * Filter out keys returned by the query which should not appear in the entity's attributes +	 * +	 * @param array $row All columns from the query +	 * @return array Columns acceptable for the entity's attributes +	 */ +	protected function filterAddedColumns($row) { +		// make an array with keys as acceptable attribute names +		$acceptable_attrs = self::$primary_attr_names; +		array_splice($acceptable_attrs, count($acceptable_attrs), 0, $this->secondary_attr_names); +		$acceptable_attrs = array_combine($acceptable_attrs, $acceptable_attrs); + +		// @todo remove these when #4584 is in place +		$acceptable_attrs['tables_split'] = true; +		$acceptable_attrs['tables_loaded'] = true; + +		foreach ($row as $key => $val) { +			if (!isset($acceptable_attrs[$key])) { +				unset($row[$key]); +			} +		} +		return $row; +	}  } diff --git a/engine/classes/ElggAutoP.php b/engine/classes/ElggAutoP.php index 89d77e583..05842d1b2 100644 --- a/engine/classes/ElggAutoP.php +++ b/engine/classes/ElggAutoP.php @@ -7,6 +7,9 @@   *   * In DIV elements, Ps are only added when there would be at   * least two of them. + *  + * @package    Elgg.Core + * @subpackage Output   */  class ElggAutoP { @@ -51,8 +54,12 @@ class ElggAutoP {  	protected $_alterList = 'article aside blockquote body details div footer header  		section'; +	/** @var string */  	protected $_unique = ''; +	/** +	 * Constructor +	 */  	public function __construct() {  		$this->_blocks = preg_split('@\\s+@', $this->_blocks);  		$this->_descendList = preg_split('@\\s+@', $this->_descendList); @@ -98,25 +105,34 @@ class ElggAutoP {  		$html = str_replace('&', $this->_unique . 'AMP', $html);  		$this->_doc = new DOMDocument(); -	    +  		// parse to DOM, suppressing loadHTML warnings  		// http://www.php.net/manual/en/domdocument.loadhtml.php#95463  		libxml_use_internal_errors(true); +		// Do not load entities. May be unnecessary, better safe than sorry +		$disable_load_entities = libxml_disable_entity_loader(true); +  		if (!$this->_doc->loadHTML("<html><meta http-equiv='content-type' "   				. "content='text/html; charset={$this->encoding}'><body>{$html}</body>"  				. "</html>")) { + +			libxml_disable_entity_loader($disable_load_entities);  			return false;  		} +		libxml_disable_entity_loader($disable_load_entities); +  		$this->_xpath = new DOMXPath($this->_doc);  		// start processing recursively at the BODY element  		$nodeList = $this->_xpath->query('//body[1]'); -		$this->_addParagraphs($nodeList->item(0)); +		$this->addParagraphs($nodeList->item(0));  		// serialize back to HTML  		$html = $this->_doc->saveHTML(); +		// Note: we create <autop> elements, which will later be converted to paragraphs +  		// split AUTOPs into multiples at /\n\n+/  		$html = preg_replace('/(' . $this->_unique . 'NL){2,}/', '</autop><autop>', $html);  		$html = str_replace(array($this->_unique . 'BR', $this->_unique . 'NL', '<br>'),  @@ -126,14 +142,22 @@ class ElggAutoP {  		// re-parse so we can handle new AUTOP elements +		// Do not load entities. May be unnecessary, better safe than sorry +		$disable_load_entities = libxml_disable_entity_loader(true); +  		if (!$this->_doc->loadHTML($html)) { +			libxml_disable_entity_loader($disable_load_entities);  			return false;  		} + +		libxml_disable_entity_loader($disable_load_entities); +  		// must re-create XPath object after DOM load  		$this->_xpath = new DOMXPath($this->_doc);  		// strip AUTOPs that only have comments/whitespace  		foreach ($this->_xpath->query('//autop') as $autop) { +			/* @var DOMElement $autop */  			$hasContent = false;  			if (trim($autop->textContent) !== '') {  				$hasContent = true; @@ -146,17 +170,19 @@ class ElggAutoP {  				}  			}  			if (!$hasContent) { -				// strip w/ preg_replace later (faster than moving nodes out) +				// mark to be later replaced w/ preg_replace (faster than moving nodes out)  				$autop->setAttribute("r", "1");  			}  		} -		// remove a single AUTOP inside certain elements +		// If a DIV contains a single AUTOP, remove it  		foreach ($this->_xpath->query('//div') as $el) { +			/* @var DOMElement $el */  			$autops = $this->_xpath->query('./autop', $el);  			if ($autops->length === 1) { -				// strip w/ preg_replace later (faster than moving nodes out) -				$autops->item(0)->setAttribute("r", "1"); +				$firstAutop = $autops->item(0); +				/* @var DOMElement $firstAutop */ +				$firstAutop->setAttribute("r", "1");  			}  		} @@ -182,15 +208,16 @@ class ElggAutoP {  	/**  	 * Add P and BR elements as necessary  	 * -	 * @param DOMElement $el +	 * @param DOMElement $el DOM element +	 * @return void  	 */ -	protected function _addParagraphs(DOMElement $el) { -		// no need to recurse, just queue up +	protected function addParagraphs(DOMElement $el) { +		// no need to call recursively, just queue up  		$elsToProcess = array($el);  		$inlinesToProcess = array();  		while ($el = array_shift($elsToProcess)) {  			// if true, we can alter all child nodes, if not, we'll just call -			// _addParagraphs on each element in the descendInto list +			// addParagraphs on each element in the descendInto list  			$alterInline = in_array($el->nodeName, $this->_alterList);  			// inside affected elements, we want to trim leading whitespace from @@ -216,16 +243,16 @@ class ElggAutoP {  				$isElement = ($node->nodeType === XML_ELEMENT_NODE);  				if ($isElement) { -					$elName = $node->nodeName; +					$isBlock = in_array($node->nodeName, $this->_blocks); +				} else { +					$isBlock = false;  				} -				$isBlock = ($isElement && in_array($elName, $this->_blocks));  				if ($alterInline) { -					$isInline = $isElement && ! $isBlock;  					$isText = ($node->nodeType === XML_TEXT_NODE);  					$isLastInline = (! $node->nextSibling -								   || ($node->nextSibling->nodeType === XML_ELEMENT_NODE -									   && in_array($node->nextSibling->nodeName, $this->_blocks))); +							|| ($node->nextSibling->nodeType === XML_ELEMENT_NODE +								&& in_array($node->nextSibling->nodeName, $this->_blocks)));  					if ($isElement) {  						$isFollowingBr = ($node->nodeName === 'br');  					} @@ -258,7 +285,7 @@ class ElggAutoP {  					if ($isBlock) {  						if (in_array($node->nodeName, $this->_descendList)) {  							$elsToProcess[] = $node; -							//$this->_addParagraphs($node); +							//$this->addParagraphs($node);  						}  					}  					$openP = true; diff --git a/engine/classes/ElggBatch.php b/engine/classes/ElggBatch.php index c1a77a0d9..d810ea066 100644 --- a/engine/classes/ElggBatch.php +++ b/engine/classes/ElggBatch.php @@ -150,6 +150,20 @@ class ElggBatch  	private $incrementOffset = true;  	/** +	 * Entities that could not be instantiated during a fetch +	 * +	 * @var stdClass[] +	 */ +	private $incompleteEntities = array(); + +	/** +	 * Total number of incomplete entities fetched +	 * +	 * @var int +	 */ +	private $totalIncompletes = 0; + +	/**  	 * Batches operations on any elgg_get_*() or compatible function that supports  	 * an options array.  	 * @@ -222,16 +236,22 @@ class ElggBatch  	}  	/** +	 * Tell the process that an entity was incomplete during a fetch +	 * +	 * @param stdClass $row +	 * +	 * @access private +	 */ +	public function reportIncompleteEntity(stdClass $row) { +		$this->incompleteEntities[] = $row; +	} + +	/**  	 * Fetches the next chunk of results  	 *  	 * @return bool  	 */  	private function getNextResultsChunk() { -		// reset memory caches after first chunk load -		if ($this->chunkIndex > 0) { -			global $DB_QUERY_CACHE, $ENTITY_CACHE; -			$DB_QUERY_CACHE = $ENTITY_CACHE = array(); -		}  		// always reset results.  		$this->results = array(); @@ -265,27 +285,47 @@ class ElggBatch  		if ($this->incrementOffset) {  			$offset = $this->offset + $this->retrievedResults;  		} else { -			$offset = $this->offset; +			$offset = $this->offset + $this->totalIncompletes;  		}  		$current_options = array(  			'limit' => $limit, -			'offset' => $offset +			'offset' => $offset, +			'__ElggBatch' => $this,  		);  		$options = array_merge($this->options, $current_options); -		$getter = $this->getter; -		if (is_string($getter)) { -			$this->results = $getter($options); -		} else { -			$this->results = call_user_func_array($getter, array($options)); +		$this->incompleteEntities = array(); +		$this->results = call_user_func_array($this->getter, array($options)); + +		$num_results = count($this->results); +		$num_incomplete = count($this->incompleteEntities); + +		$this->totalIncompletes += $num_incomplete; + +		if ($this->incompleteEntities) { +			// pad the front of the results with nulls representing the incompletes +			array_splice($this->results, 0, 0, array_pad(array(), $num_incomplete, null)); +			// ...and skip past them +			reset($this->results); +			for ($i = 0; $i < $num_incomplete; $i++) { +				next($this->results); +			}  		}  		if ($this->results) {  			$this->chunkIndex++; -			$this->resultIndex = 0; -			$this->retrievedResults += count($this->results); + +			// let the system know we've jumped past the nulls +			$this->resultIndex = $num_incomplete; + +			$this->retrievedResults += ($num_results + $num_incomplete); +			if ($num_results == 0) { +				// This fetch was *all* incompletes! We need to fetch until we can either +				// offer at least one row to iterate over, or give up. +				return $this->getNextResultsChunk(); +			}  			return true;  		} else {  			return false; @@ -296,7 +336,8 @@ class ElggBatch  	 * Increment the offset from the original options array? Setting to  	 * false is required for callbacks that delete rows.  	 * -	 * @param bool $increment +	 * @param bool $increment Set to false when deleting data +	 * @return void  	 */  	public function setIncrementOffset($increment = true) {  		$this->incrementOffset = (bool) $increment; diff --git a/engine/classes/ElggCache.php b/engine/classes/ElggCache.php index 4317f4be9..909eab39b 100644 --- a/engine/classes/ElggCache.php +++ b/engine/classes/ElggCache.php @@ -21,6 +21,7 @@ abstract class ElggCache implements ArrayAccess {  		$this->variables = array();  	} +	// @codingStandardsIgnoreStart  	/**  	 * Set a cache variable.  	 * @@ -35,6 +36,7 @@ abstract class ElggCache implements ArrayAccess {  		elgg_deprecated_notice('ElggCache::set_variable() is deprecated by ElggCache::setVariable()', 1.8);  		$this->setVariable($variable, $value);  	} +	// @codingStandardsIgnoreEnd  	/**  	 * Set a cache variable. @@ -52,6 +54,7 @@ abstract class ElggCache implements ArrayAccess {  		$this->variables[$variable] = $value;  	} +	// @codingStandardsIgnoreStart  	/**  	 * Get variables for this cache.  	 * @@ -65,6 +68,7 @@ abstract class ElggCache implements ArrayAccess {  		elgg_deprecated_notice('ElggCache::get_variable() is deprecated by ElggCache::getVariable()', 1.8);  		return $this->getVariable($variable);  	} +	// @codingStandardsIgnoreEnd  	/**  	 * Get variables for this cache. diff --git a/engine/classes/ElggCrypto.php b/engine/classes/ElggCrypto.php new file mode 100644 index 000000000..317d371e4 --- /dev/null +++ b/engine/classes/ElggCrypto.php @@ -0,0 +1,208 @@ +<?php +/** + * ElggCrypto + * + * @package    Elgg.Core + * @subpackage Crypto + * + * @access private + */ +class ElggCrypto { + +	/** +	 * Character set for temp passwords (no risk of embedded profanity/glyphs that look similar) +	 */ +	const CHARS_PASSWORD = 'bcdfghjklmnpqrstvwxyz2346789'; + +	/** +	 * Generate a string of highly randomized bytes (over the full 8-bit range). +	 * +	 * @param int $length Number of bytes needed +	 * @return string Random bytes +	 * +	 * @author George Argyros <argyros.george@gmail.com> +	 * @copyright 2012, George Argyros. All rights reserved. +	 * @license Modified BSD +	 * @link https://github.com/GeorgeArgyros/Secure-random-bytes-in-PHP/blob/master/srand.php Original +	 * +	 * Redistribution and use in source and binary forms, with or without +	 * modification, are permitted provided that the following conditions are met: +	 *    * Redistributions of source code must retain the above copyright +	 *      notice, this list of conditions and the following disclaimer. +	 *    * Redistributions in binary form must reproduce the above copyright +	 *      notice, this list of conditions and the following disclaimer in the +	 *      documentation and/or other materials provided with the distribution. +	 *    * Neither the name of the <organization> nor the +	 *      names of its contributors may be used to endorse or promote products +	 *      derived from this software without specific prior written permission. +	 * +	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +	 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +	 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +	 * DISCLAIMED. IN NO EVENT SHALL GEORGE ARGYROS BE LIABLE FOR ANY +	 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +	 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +	 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +	 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +	 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +	 */ +	public function getRandomBytes($length) { +		/** +		 * Our primary choice for a cryptographic strong randomness function is +		 * openssl_random_pseudo_bytes. +		 */ +		$SSLstr = '4'; // http://xkcd.com/221/ +		if (function_exists('openssl_random_pseudo_bytes') +				&& (version_compare(PHP_VERSION, '5.3.4') >= 0 || substr(PHP_OS, 0, 3) !== 'WIN')) { +			$SSLstr = openssl_random_pseudo_bytes($length, $strong); +			if ($strong) { +				return $SSLstr; +			} +		} + +		/** +		 * If mcrypt extension is available then we use it to gather entropy from +		 * the operating system's PRNG. This is better than reading /dev/urandom +		 * directly since it avoids reading larger blocks of data than needed. +		 * Older versions of mcrypt_create_iv may be broken or take too much time +		 * to finish so we only use this function with PHP 5.3.7 and above. +		 * @see https://bugs.php.net/bug.php?id=55169 +		 */ +		if (function_exists('mcrypt_create_iv') +				&& (version_compare(PHP_VERSION, '5.3.7') >= 0 || substr(PHP_OS, 0, 3) !== 'WIN')) { +			$str = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); +			if ($str !== false) { +				return $str; +			} +		} + +		/** +		 * No build-in crypto randomness function found. We collect any entropy +		 * available in the PHP core PRNGs along with some filesystem info and memory +		 * stats. To make this data cryptographically strong we add data either from +		 * /dev/urandom or if its unavailable, we gather entropy by measuring the +		 * time needed to compute a number of SHA-1 hashes. +		 */ +		$str = ''; +		$bits_per_round = 2; // bits of entropy collected in each clock drift round +		$msec_per_round = 400; // expected running time of each round in microseconds +		$hash_len = 20; // SHA-1 Hash length +		$total = $length; // total bytes of entropy to collect + +		$handle = @fopen('/dev/urandom', 'rb'); +		if ($handle && function_exists('stream_set_read_buffer')) { +			@stream_set_read_buffer($handle, 0); +		} + +		do { +			$bytes = ($total > $hash_len) ? $hash_len : $total; +			$total -= $bytes; + +			//collect any entropy available from the PHP system and filesystem +			$entropy = rand() . uniqid(mt_rand(), true) . $SSLstr; +			$entropy .= implode('', @fstat(@fopen(__FILE__, 'r'))); +			$entropy .= memory_get_usage() . getmypid(); +			$entropy .= serialize($_ENV) . serialize($_SERVER); +			if (function_exists('posix_times')) { +				$entropy .= serialize(posix_times()); +			} +			if (function_exists('zend_thread_id')) { +				$entropy .= zend_thread_id(); +			} + +			if ($handle) { +				$entropy .= @fread($handle, $bytes); +			} else { +				// Measure the time that the operations will take on average +				for ($i = 0; $i < 3; $i++) { +					$c1 = microtime(true); +					$var = sha1(mt_rand()); +					for ($j = 0; $j < 50; $j++) { +						$var = sha1($var); +					} +					$c2 = microtime(true); +					$entropy .= $c1 . $c2; +				} + +				// Based on the above measurement determine the total rounds +				// in order to bound the total running time. +				$rounds = (int) ($msec_per_round * 50 / (int) (($c2 - $c1) * 1000000)); + +				// Take the additional measurements. On average we can expect +				// at least $bits_per_round bits of entropy from each measurement. +				$iter = $bytes * (int) (ceil(8 / $bits_per_round)); + +				for ($i = 0; $i < $iter; $i++) { +					$c1 = microtime(); +					$var = sha1(mt_rand()); +					for ($j = 0; $j < $rounds; $j++) { +						$var = sha1($var); +					} +					$c2 = microtime(); +					$entropy .= $c1 . $c2; +				} +			} + +			// We assume sha1 is a deterministic extractor for the $entropy variable. +			$str .= sha1($entropy, true); + +		} while ($length > strlen($str)); + +		if ($handle) { +			@fclose($handle); +		} + +		return substr($str, 0, $length); +	} + +	/** +	 * Generate a random string of specified length. +	 * +	 * Uses supplied character list for generating the new string. +	 * If no character list provided - uses Base64 URL character set. +	 * +	 * @param int         $length Desired length of the string +	 * @param string|null $chars  Characters to be chosen from randomly. If not given, the Base64 URL +	 *                            charset will be used. +	 * +	 * @return string The random string +	 * +	 * @throws InvalidArgumentException +	 * +	 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) +	 * @license   http://framework.zend.com/license/new-bsd New BSD License +	 * +	 * @see https://github.com/zendframework/zf2/blob/master/library/Zend/Math/Rand.php#L179 +	 */ +	public static function getRandomString($length, $chars = null) { +		if ($length < 1) { +			throw new InvalidArgumentException('Length should be >= 1'); +		} + +		if (empty($chars)) { +			$numBytes = ceil($length * 0.75); +			$bytes    = self::getRandomBytes($numBytes); +			$string = substr(rtrim(base64_encode($bytes), '='), 0, $length); + +			// Base64 URL +			return strtr($string, '+/', '-_'); +		} + +		$listLen = strlen($chars); + +		if ($listLen == 1) { +			return str_repeat($chars, $length); +		} + +		$bytes  = self::getRandomBytes($length); +		$pos    = 0; +		$result = ''; +		for ($i = 0; $i < $length; $i++) { +			$pos     = ($pos + ord($bytes[$i])) % $listLen; +			$result .= $chars[$pos]; +		} + +		return $result; +	} +} diff --git a/engine/classes/ElggData.php b/engine/classes/ElggData.php index 3470ee1cf..4f843cde4 100644 --- a/engine/classes/ElggData.php +++ b/engine/classes/ElggData.php @@ -5,6 +5,9 @@   *   * @package    Elgg.Core   * @subpackage DataModel + * + * @property int $owner_guid + * @property int $time_created   */  abstract class ElggData implements  	Loggable,	// Can events related to this object class be logged @@ -23,6 +26,7 @@ abstract class ElggData implements  	 */  	protected $attributes = array(); +	// @codingStandardsIgnoreStart  	/**  	 * Initialise the attributes array.  	 * @@ -33,16 +37,15 @@ abstract class ElggData implements  	 *                        Passing false returns false.  Core constructors always pass false.  	 *                        Does nothing either way since attributes are initialized by the time  	 *                        this is called. -	 * @return false|void False is +	 * @return void  	 * @deprecated 1.8 Use initializeAttributes()  	 */  	protected function initialise_attributes($pre18_api = true) {  		if ($pre18_api) {  			elgg_deprecated_notice('initialise_attributes() is deprecated by initializeAttributes()', 1.8); -		} else { -			return false;  		}  	} +	// @codingStandardsIgnoreEnd  	/**  	 * Initialize the attributes array. @@ -111,7 +114,7 @@ abstract class ElggData implements  	 * @param string $name  The attribute to set  	 * @param mixed  $value The value to set it to  	 * -	 * @return The success of your set funtion? +	 * @return bool The success of your set function?  	 */  	abstract protected function set($name, $value); @@ -195,7 +198,7 @@ abstract class ElggData implements  	 *  	 * @see Iterator::current()  	 * -	 * @return void +	 * @return mixed  	 */  	public function current() {  		return current($this->attributes); @@ -206,7 +209,7 @@ abstract class ElggData implements  	 *  	 * @see Iterator::key()  	 * -	 * @return void +	 * @return string  	 */  	public function key() {  		return key($this->attributes); @@ -228,7 +231,7 @@ abstract class ElggData implements  	 *  	 * @see Iterator::valid()  	 * -	 * @return void +	 * @return bool  	 */  	public function valid() {  		return $this->valid; @@ -266,12 +269,13 @@ abstract class ElggData implements  	 *  	 * @param mixed $key Name  	 * -	 * @return void +	 * @return mixed  	 */  	public function offsetGet($key) {  		if (array_key_exists($key, $this->attributes)) {  			return $this->attributes[$key];  		} +		return null;  	}  	/** diff --git a/engine/classes/ElggDiskFilestore.php b/engine/classes/ElggDiskFilestore.php index 7aace43ba..6e2354012 100644 --- a/engine/classes/ElggDiskFilestore.php +++ b/engine/classes/ElggDiskFilestore.php @@ -60,6 +60,7 @@ class ElggDiskFilestore extends ElggFilestore {  		$path = substr($fullname, 0, $ls);  		$name = substr($fullname, $ls); +		// @todo $name is unused, remove it or do we need to fix something?  		// Try and create the directory  		try { @@ -108,7 +109,7 @@ class ElggDiskFilestore extends ElggFilestore {  	 *  	 * @param resource $f      File pointer resource  	 * @param int      $length The number of bytes to read -	 * @param inf      $offset The number of bytes to start after +	 * @param int      $offset The number of bytes to start after  	 *  	 * @return mixed Contents of file or false on fail.  	 */ @@ -193,11 +194,14 @@ class ElggDiskFilestore extends ElggFilestore {  	}  	/** -	 * Returns the filename as saved on disk for an ElggFile object +	 * Get the filename as saved on disk for an ElggFile object +	 * +	 * Returns an empty string if no filename set  	 *  	 * @param ElggFile $file File object  	 *  	 * @return string The full path of where the file is stored +	 * @throws InvalidParameterException  	 */  	public function getFilenameOnFilestore(ElggFile $file) {  		$owner_guid = $file->getOwnerGuid(); @@ -211,7 +215,12 @@ class ElggDiskFilestore extends ElggFilestore {  			throw new InvalidParameterException($msg);  		} -		return $this->dir_root . $this->makefileMatrix($owner_guid) . $file->getFilename(); +		$filename = $file->getFilename(); +		if (!$filename) { +			return ''; +		} + +		return $this->dir_root . $this->makeFileMatrix($owner_guid) . $filename;  	}  	/** @@ -219,7 +228,7 @@ class ElggDiskFilestore extends ElggFilestore {  	 *  	 * @param ElggFile $file File object  	 * -	 * @return mixed +	 * @return string  	 */  	public function grabFile(ElggFile $file) {  		return file_get_contents($file->getFilenameOnFilestore()); @@ -233,6 +242,9 @@ class ElggDiskFilestore extends ElggFilestore {  	 * @return bool  	 */  	public function exists(ElggFile $file) { +		if (!$file->getFilename()) { +			return false; +		}  		return file_exists($this->getFilenameOnFilestore($file));  	} @@ -246,12 +258,13 @@ class ElggDiskFilestore extends ElggFilestore {  	 */  	public function getSize($prefix = '', $container_guid) {  		if ($container_guid) { -			return get_dir_size($this->dir_root . $this->makefileMatrix($container_guid) . $prefix); +			return get_dir_size($this->dir_root . $this->makeFileMatrix($container_guid) . $prefix);  		} else {  			return false;  		}  	} +	// @codingStandardsIgnoreStart  	/**  	 * Create a directory $dirroot  	 * @@ -266,6 +279,7 @@ class ElggDiskFilestore extends ElggFilestore {  		return $this->makeDirectoryRoot($dirroot);  	} +	// @codingStandardsIgnoreEnd  	/**  	 * Create a directory $dirroot @@ -285,6 +299,7 @@ class ElggDiskFilestore extends ElggFilestore {  		return true;  	} +	// @codingStandardsIgnoreStart  	/**  	 * Multibyte string tokeniser.  	 * @@ -315,30 +330,31 @@ class ElggDiskFilestore extends ElggFilestore {  		} else {  			return str_split($string);  		} - -		return false;  	} +	// @codingStandardsIgnoreEnd +	// @codingStandardsIgnoreStart  	/**  	 * Construct a file path matrix for an entity.  	 *  	 * @param int $identifier The guide of the entity to store the data under.  	 * -	 * @return str The path where the entity's data will be stored. +	 * @return string The path where the entity's data will be stored.  	 * @deprecated 1.8 Use ElggDiskFilestore::makeFileMatrix()  	 */  	protected function make_file_matrix($identifier) {  		elgg_deprecated_notice('ElggDiskFilestore::make_file_matrix() is deprecated by ::makeFileMatrix()', 1.8); -		return $this->makefileMatrix($identifier); +		return $this->makeFileMatrix($identifier);  	} +	// @codingStandardsIgnoreEnd  	/**  	 * Construct a file path matrix for an entity.  	 *  	 * @param int $guid The guide of the entity to store the data under.  	 * -	 * @return str The path where the entity's data will be stored. +	 * @return string The path where the entity's data will be stored.  	 */  	protected function makeFileMatrix($guid) {  		$entity = get_entity($guid); @@ -352,6 +368,7 @@ class ElggDiskFilestore extends ElggFilestore {  		return "$time_created/$entity->guid/";  	} +	// @codingStandardsIgnoreStart  	/**  	 * Construct a filename matrix.  	 * @@ -363,13 +380,14 @@ class ElggDiskFilestore extends ElggFilestore {  	 *  	 * @param int $guid The entity to contrust a matrix for  	 * -	 * @return str The +	 * @return string The  	 */  	protected function user_file_matrix($guid) {  		elgg_deprecated_notice('ElggDiskFilestore::user_file_matrix() is deprecated by ::makeFileMatrix()', 1.8);  		return $this->makeFileMatrix($guid);  	} +	// @codingStandardsIgnoreEnd  	/**  	 * Returns a list of attributes to save to the database when saving diff --git a/engine/classes/ElggEntity.php b/engine/classes/ElggEntity.php index 929abceb2..a563f6fad 100644 --- a/engine/classes/ElggEntity.php +++ b/engine/classes/ElggEntity.php @@ -24,7 +24,7 @@   *   * @package    Elgg.Core   * @subpackage DataModel.Entities - *  + *   * @property string $type           object, user, group, or site (read-only after save)   * @property string $subtype        Further clarifies the nature of the entity (read-only after save)   * @property int    $guid           The unique identifier for this entity (read only) @@ -34,6 +34,7 @@   * @property int    $access_id      Specifies the visibility level of this entity   * @property int    $time_created   A UNIX timestamp of when the entity was created (read-only, set on first save)   * @property int    $time_updated   A UNIX timestamp of when the entity was last updated (automatically updated on save) + * @property-read string $enabled   */  abstract class ElggEntity extends ElggData implements  	Notable,    // Calendar interface @@ -351,8 +352,8 @@ abstract class ElggEntity extends ElggData implements  					'limit' => 0  				);  				// @todo in 1.9 make this return false if can't add metadata -				// http://trac.elgg.org/ticket/4520 -				//  +				// https://github.com/elgg/elgg/issues/4520 +				//  				// need to remove access restrictions right now to delete  				// because this is the expected behavior  				$ia = elgg_set_ignore_access(true); @@ -374,12 +375,11 @@ abstract class ElggEntity extends ElggData implements  			}  			return $result; -		} - -		// unsaved entity. store in temp array -		// returning single entries instead of an array of 1 element is decided in -		// getMetaData(), just like pulling from the db. -		else { +		} else { +			// unsaved entity. store in temp array +			// returning single entries instead of an array of 1 element is decided in +			// getMetaData(), just like pulling from the db. +			//  			// if overwrite, delete first  			if (!$multiple || !isset($this->temp_metadata[$name])) {  				$this->temp_metadata[$name] = array(); @@ -940,7 +940,7 @@ abstract class ElggEntity extends ElggData implements  	 * @param ElggMetadata $metadata  The piece of metadata to specifically check  	 * @param int          $user_guid The user GUID, optionally (default: logged in user)  	 * -	 * @return true|false +	 * @return bool  	 */  	function canEditMetadata($metadata = null, $user_guid = 0) {  		return can_edit_entity_metadata($this->getGUID(), $user_guid, $metadata); @@ -964,7 +964,7 @@ abstract class ElggEntity extends ElggData implements  	 *  	 * @tip Can be overridden by registering for the permissions_check:comment,  	 * <entity type> plugin hook. -	 *  +	 *  	 * @param int $user_guid User guid (default is logged in user)  	 *  	 * @return bool @@ -1270,15 +1270,23 @@ abstract class ElggEntity extends ElggData implements  	public function save() {  		$guid = $this->getGUID();  		if ($guid > 0) { -			cache_entity($this); -			return update_entity( +			// See #5600. This ensures the lower level can_edit_entity() check will use a +			// fresh entity from the DB so it sees the persisted owner_guid +			_elgg_disable_caching_for_entity($guid); + +			$ret = update_entity(  				$guid,  				$this->get('owner_guid'),  				$this->get('access_id'),  				$this->get('container_guid'),  				$this->get('time_created')  			); + +			_elgg_enable_caching_for_entity($guid); +			_elgg_cache_entity($this); + +			return $ret;  		} else {  			// Create a new entity (nb: using attribute array directly  			// 'cos set function does something special!) @@ -1320,7 +1328,7 @@ abstract class ElggEntity extends ElggData implements  			$this->attributes['subtype'] = get_subtype_id($this->attributes['type'],  				$this->attributes['subtype']); -			cache_entity($this); +			_elgg_cache_entity($this);  			return $this->attributes['guid'];  		} @@ -1357,12 +1365,12 @@ abstract class ElggEntity extends ElggData implements  				$this->attributes['tables_loaded']++;  			} -			// guid needs to be an int  http://trac.elgg.org/ticket/4111 +			// guid needs to be an int  https://github.com/elgg/elgg/issues/4111  			$this->attributes['guid'] = (int)$this->attributes['guid'];  			// Cache object handle  			if ($this->attributes['guid']) { -				cache_entity($this); +				_elgg_cache_entity($this);  			}  			return true; @@ -1668,9 +1676,11 @@ abstract class ElggEntity extends ElggData implements  	/**  	 * Import data from an parsed ODD xml data array.  	 * -	 * @param array $data XML data +	 * @param ODD $data XML data  	 *  	 * @return true +	 * +	 * @throws InvalidParameterException  	 */  	public function import(ODD $data) {  		if (!($data instanceof ODDEntity)) { @@ -1732,8 +1742,6 @@ abstract class ElggEntity extends ElggData implements  	 * @return array  	 */  	public function getTags($tag_names = NULL) { -		global $CONFIG; -  		if ($tag_names && !is_array($tag_names)) {  			$tag_names = array($tag_names);  		} diff --git a/engine/classes/ElggExtender.php b/engine/classes/ElggExtender.php index d94bad837..25aba354f 100644 --- a/engine/classes/ElggExtender.php +++ b/engine/classes/ElggExtender.php @@ -171,7 +171,7 @@ abstract class ElggExtender extends ElggData {  	public function export() {  		$uuid = get_uuid_from_object($this); -		$meta = new ODDMetadata($uuid, guid_to_uuid($this->entity_guid), $this->attributes['name'], +		$meta = new ODDMetaData($uuid, guid_to_uuid($this->entity_guid), $this->attributes['name'],  			$this->attributes['value'], $this->attributes['type'], guid_to_uuid($this->owner_guid));  		$meta->setAttribute('published', date("r", $this->time_created)); diff --git a/engine/classes/ElggFile.php b/engine/classes/ElggFile.php index f21621ffd..23080834b 100644 --- a/engine/classes/ElggFile.php +++ b/engine/classes/ElggFile.php @@ -93,6 +93,7 @@ class ElggFile extends ElggObject {  			$container_guid = $this->container_guid;  		}  		$fs = $this->getFilestore(); +		// @todo add getSize() to ElggFilestore  		return $fs->getSize($prefix, $container_guid);  	} @@ -127,9 +128,11 @@ class ElggFile extends ElggObject {  	 * @param mixed $default A default. Useful to pass what the browser thinks it is.  	 * @since 1.7.12  	 * +	 * @note If $file is provided, this may be called statically +	 *  	 * @return mixed Detected type on success, false on failure.  	 */ -	static function detectMimeType($file = null, $default = null) { +	public function detectMimeType($file = null, $default = null) {  		if (!$file) {  			if (isset($this) && $this->filename) {  				$file = $this->filename; @@ -178,6 +181,8 @@ class ElggFile extends ElggObject {  	 * @param string $mode Either read/write/append  	 *  	 * @return resource File handler +	 * +	 * @throws IOException|InvalidParameterException  	 */  	public function open($mode) {  		if (!$this->getFilename()) { @@ -270,9 +275,14 @@ class ElggFile extends ElggObject {  	 */  	public function delete() {  		$fs = $this->getFilestore(); -		if ($fs->delete($this)) { -			return parent::delete(); +		 +		$result = $fs->delete($this); +		 +		if ($this->getGUID() && $result) { +			$result = parent::delete();  		} +		 +		return $result;  	}  	/** @@ -285,6 +295,7 @@ class ElggFile extends ElggObject {  	public function seek($position) {  		$fs = $this->getFilestore(); +		// @todo add seek() to ElggFilestore  		return $fs->seek($this->handle, $position);  	} @@ -347,6 +358,8 @@ class ElggFile extends ElggObject {  	 * a filestore as recorded in metadata or the system default.  	 *  	 * @return ElggFilestore +	 * +	 * @throws ClassNotFoundException  	 */  	protected function getFilestore() {  		// Short circuit if already set. @@ -359,7 +372,6 @@ class ElggFile extends ElggObject {  		// need to get all filestore::* metadata because the rest are "parameters" that  		// get passed to filestore::setParameters()  		if ($this->guid) { -			$db_prefix = elgg_get_config('dbprefix');  			$options = array(  				'guid' => $this->guid,  				'where' => array("n.string LIKE 'filestore::%'"), @@ -388,6 +400,7 @@ class ElggFile extends ElggObject {  			$this->filestore = new $filestore();  			$this->filestore->setParameters($parameters); +			// @todo explain why $parameters will always be set here (PhpStorm complains)  		}  		// this means the entity hasn't been saved so fallback to default diff --git a/engine/classes/ElggFileCache.php b/engine/classes/ElggFileCache.php index 34178d452..94143f777 100644 --- a/engine/classes/ElggFileCache.php +++ b/engine/classes/ElggFileCache.php @@ -13,6 +13,8 @@ class ElggFileCache extends ElggCache {  	 * @param string $cache_path The cache path.  	 * @param int    $max_age    Maximum age in seconds, 0 if no limit.  	 * @param int    $max_size   Maximum size of cache in seconds, 0 if no limit. +	 * +	 * @throws ConfigurationException  	 */  	function __construct($cache_path, $max_age = 0, $max_size = 0) {  		$this->setVariable("cache_path", $cache_path); @@ -24,6 +26,7 @@ class ElggFileCache extends ElggCache {  		}  	} +	// @codingStandardsIgnoreStart  	/**  	 * Create and return a handle to a file.  	 * @@ -39,6 +42,7 @@ class ElggFileCache extends ElggCache {  		return $this->createFile($filename, $rw);  	} +	// @codingStandardsIgnoreEnd  	/**  	 * Create and return a handle to a file. @@ -70,6 +74,7 @@ class ElggFileCache extends ElggCache {  		return fopen($path . $filename, $rw);  	} +	// @codingStandardsIgnoreStart  	/**  	 * Create a sanitised filename for the file.  	 * @@ -84,6 +89,7 @@ class ElggFileCache extends ElggCache {  		return $filename;  	} +	// @codingStandardsIgnoreEnd  	/**  	 * Create a sanitised filename for the file. diff --git a/engine/classes/ElggGroup.php b/engine/classes/ElggGroup.php index ea257f368..7e69b7a84 100644 --- a/engine/classes/ElggGroup.php +++ b/engine/classes/ElggGroup.php @@ -32,7 +32,7 @@ class ElggGroup extends ElggEntity  	 * @param mixed $guid If an int, load that GUID.  	 * 	If an entity table db row, then will load the rest of the data.  	 * -	 * @throws Exception if there was a problem creating the group. +	 * @throws IOException|InvalidParameterException if there was a problem creating the group.  	 */  	function __construct($guid = null) {  		$this->initializeAttributes(); @@ -48,21 +48,18 @@ class ElggGroup extends ElggEntity  					$msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));  					throw new IOException($msg);  				} - -			// Is $guid is an ElggGroup? Use a copy constructor  			} else if ($guid instanceof ElggGroup) { +				// $guid is an ElggGroup so this is a copy constructor  				elgg_deprecated_notice('This type of usage of the ElggGroup constructor was deprecated. Please use the clone method.', 1.7);  				foreach ($guid->attributes as $key => $value) {  					$this->attributes[$key] = $value;  				} - -			// Is this is an ElggEntity but not an ElggGroup = ERROR!  			} else if ($guid instanceof ElggEntity) { +				// @todo why separate from else  				throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggGroup')); - -			// Is it a GUID  			} else if (is_numeric($guid)) { +				// $guid is a GUID so load entity  				if (!$this->load($guid)) {  					throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));  				} @@ -220,6 +217,7 @@ class ElggGroup extends ElggEntity  	 * @return array|false  	 */  	public function getObjects($subtype = "", $limit = 10, $offset = 0) { +		// @todo are we deprecating this method, too?  		return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false);  	} @@ -233,6 +231,7 @@ class ElggGroup extends ElggEntity  	 * @return array|false  	 */  	public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0) { +		// @todo are we deprecating this method, too?  		return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false);  	} @@ -244,6 +243,7 @@ class ElggGroup extends ElggEntity  	 * @return array|false  	 */  	public function countObjects($subtype = "") { +		// @todo are we deprecating this method, too?  		return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", 10, 0, true);  	} @@ -284,7 +284,7 @@ class ElggGroup extends ElggEntity  	 *  	 * @return bool  	 */ -	public function isMember($user = 0) { +	public function isMember($user = null) {  		if (!($user instanceof ElggUser)) {  			$user = elgg_get_logged_in_user_entity();  		} @@ -335,7 +335,7 @@ class ElggGroup extends ElggEntity  		$this->attributes = $attrs;  		$this->attributes['tables_loaded'] = 2; -		cache_entity($this); +		_elgg_cache_entity($this);  		return true;  	} @@ -352,7 +352,12 @@ class ElggGroup extends ElggEntity  		}  		// Now save specific stuff -		return create_group_entity($this->get('guid'), $this->get('name'), $this->get('description')); + +		_elgg_disable_caching_for_entity($this->guid); +		$ret = create_group_entity($this->get('guid'), $this->get('name'), $this->get('description')); +		_elgg_enable_caching_for_entity($this->guid); + +		return $ret;  	}  	// EXPORTABLE INTERFACE //////////////////////////////////////////////////////////// diff --git a/engine/classes/ElggLRUCache.php b/engine/classes/ElggLRUCache.php new file mode 100644 index 000000000..f51af2ed7 --- /dev/null +++ b/engine/classes/ElggLRUCache.php @@ -0,0 +1,181 @@ +<?php + +/** + * Least Recently Used Cache + * + * A fixed sized cache that removes the element used last when it reaches its + * size limit. + *  + * Based on https://github.com/cash/LRUCache + *  + * @access private + *  + * @package    Elgg.Core + * @subpackage Cache + */ +class ElggLRUCache implements ArrayAccess { +	/** @var int */ +	protected $maximumSize; + +	/** +	 * The front of the array contains the LRU element +	 * +	 * @var array +	 */ +	protected $data = array(); + +	/** +	 * Create a LRU Cache +	 * +	 * @param int $size The size of the cache +	 * @throws InvalidArgumentException +	 */ +	public function __construct($size) { +		if (!is_int($size) || $size <= 0) { +			throw new InvalidArgumentException(); +		} +		$this->maximumSize = $size; +	} + +	/** +	 * Get the value cached with this key +	 * +	 * @param int|string $key     The key. Strings that are ints are cast to ints. +	 * @param mixed      $default The value to be returned if key not found. (Optional) +	 * @return mixed +	 */ +	public function get($key, $default = null) { +		if (isset($this->data[$key])) { +			$this->recordAccess($key); +			return $this->data[$key]; +		} else { +			return $default; +		} +	} + +	/** +	 * Add something to the cache +	 * +	 * @param int|string $key   The key. Strings that are ints are cast to ints. +	 * @param mixed      $value The value to cache +	 * @return void +	 */ +	public function set($key, $value) { +		if (isset($this->data[$key])) { +			$this->data[$key] = $value; +			$this->recordAccess($key); +		} else { +			$this->data[$key] = $value; +			if ($this->size() > $this->maximumSize) { +				// remove least recently used element (front of array) +				reset($this->data); +				unset($this->data[key($this->data)]); +			} +		} +	} + +	/** +	 * Get the number of elements in the cache +	 * +	 * @return int +	 */ +	public function size() { +		return count($this->data); +	} + +	/** +	 * Does the cache contain an element with this key +	 * +	 * @param int|string $key The key +	 * @return boolean +	 */ +	public function containsKey($key) { +		return isset($this->data[$key]); +	} + +	/** +	 * Remove the element with this key. +	 * +	 * @param int|string $key The key +	 * @return mixed Value or null if not set +	 */ +	public function remove($key) { +		if (isset($this->data[$key])) { +			$value = $this->data[$key]; +			unset($this->data[$key]); +			return $value; +		} else { +			return null; +		} +	} + +	/** +	 * Clear the cache +	 *  +	 * @return void +	 */ +	public function clear() { +		$this->data = array(); +	} + +	/** +	 * Moves the element from current position to end of array +	 *  +	 * @param int|string $key The key +	 * @return void +	 */ +	protected function recordAccess($key) { +		$value = $this->data[$key]; +		unset($this->data[$key]); +		$this->data[$key] = $value; +	} + +	/** +	 * Assigns a value for the specified key +	 * +	 * @see ArrayAccess::offsetSet() +	 * +	 * @param int|string $key   The key to assign the value to. +	 * @param mixed      $value The value to set. +	 * @return void +	 */ +	public function offsetSet($key, $value) { +		$this->set($key, $value); +	} + +	/** +	 * Get the value for specified key +	 * +	 * @see ArrayAccess::offsetGet() +	 * +	 * @param int|string $key The key to retrieve. +	 * @return mixed +	 */ +	public function offsetGet($key) { +		return $this->get($key); +	} + +	/** +	 * Unsets a key. +	 * +	 * @see ArrayAccess::offsetUnset() +	 * +	 * @param int|string $key The key to unset. +	 * @return void +	 */ +	public function offsetUnset($key) { +		$this->remove($key); +	} + +	/** +	 * Does key exist? +	 * +	 * @see ArrayAccess::offsetExists() +	 * +	 * @param int|string $key A key to check for. +	 * @return boolean +	 */ +	public function offsetExists($key) { +		return $this->containsKey($key); +	} +} diff --git a/engine/classes/ElggMemcache.php b/engine/classes/ElggMemcache.php index d9539b9cb..91d50ab89 100644 --- a/engine/classes/ElggMemcache.php +++ b/engine/classes/ElggMemcache.php @@ -32,6 +32,8 @@ class ElggMemcache extends ElggSharedMemoryCache {  	 *  	 * @param string $namespace The namespace for this cache to write to -  	 * note, namespaces of the same name are shared! +	 * +	 * @throws ConfigurationException  	 */  	function __construct($namespace = 'default') {  		global $CONFIG; diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php index d9a704dd9..b463143d8 100644 --- a/engine/classes/ElggMenuBuilder.php +++ b/engine/classes/ElggMenuBuilder.php @@ -8,6 +8,9 @@   */  class ElggMenuBuilder { +	/** +	 * @var ElggMenuItem[] +	 */  	protected $menu = array();  	protected $selected = null; @@ -15,7 +18,7 @@ class ElggMenuBuilder {  	/**  	 * ElggMenuBuilder constructor  	 * -	 * @param array $menu Array of ElggMenuItem objects +	 * @param ElggMenuItem[] $menu Array of ElggMenuItem objects  	 */  	public function __construct(array $menu) {  		$this->menu = $menu; @@ -107,6 +110,7 @@ class ElggMenuBuilder {  			$children = array();  			// divide base nodes from children  			foreach ($section as $menu_item) { +				/* @var ElggMenuItem $menu_item */  				$parent_name = $menu_item->getParentName();  				if (!$parent_name) {  					$parents[$menu_item->getName()] = $menu_item; @@ -118,13 +122,16 @@ class ElggMenuBuilder {  			// attach children to parents  			$iteration = 0;  			$current_gen = $parents; +			$next_gen = null;  			while (count($children) && $iteration < 5) {  				foreach ($children as $index => $menu_item) {  					$parent_name = $menu_item->getParentName();  					if (array_key_exists($parent_name, $current_gen)) {  						$next_gen[$menu_item->getName()] = $menu_item; -						$current_gen[$parent_name]->addChild($menu_item); -						$menu_item->setParent($current_gen[$parent_name]); +						if (!in_array($menu_item, $current_gen[$parent_name]->getData('children'))) { +							$current_gen[$parent_name]->addChild($menu_item); +							$menu_item->setParent($current_gen[$parent_name]); +						}  						unset($children[$index]);  					}  				} @@ -216,12 +223,12 @@ class ElggMenuBuilder {  				array_push($stack, $root);  				while (!empty($stack)) {  					$node = array_pop($stack); +					/* @var ElggMenuItem $node */  					$node->sortChildren($sort_callback);  					$children = $node->getChildren();  					if ($children) {  						$stack = array_merge($stack, $children);  					} -					$p = count($stack);  				}  			}  		} @@ -230,8 +237,8 @@ class ElggMenuBuilder {  	/**  	 * Compare two menu items by their display text  	 * -	 * @param ElggMenuItem $a -	 * @param ElggMenuItem $b +	 * @param ElggMenuItem $a Menu item +	 * @param ElggMenuItem $b Menu item  	 * @return bool  	 */  	public static function compareByText($a, $b) { @@ -248,8 +255,8 @@ class ElggMenuBuilder {  	/**  	 * Compare two menu items by their identifiers  	 * -	 * @param ElggMenuItem $a -	 * @param ElggMenuItem $b +	 * @param ElggMenuItem $a Menu item +	 * @param ElggMenuItem $b Menu item  	 * @return bool  	 */  	public static function compareByName($a, $b) { @@ -266,9 +273,11 @@ class ElggMenuBuilder {  	/**  	 * Compare two menu items by their priority  	 * -	 * @param ElggMenuItem $a -	 * @param ElggMenuItem $b +	 * @param ElggMenuItem $a Menu item +	 * @param ElggMenuItem $b Menu item  	 * @return bool +	 * +	 * @todo change name to compareByPriority  	 */  	public static function compareByWeight($a, $b) {  		$aw = $a->getWeight(); diff --git a/engine/classes/ElggMetadata.php b/engine/classes/ElggMetadata.php index 7f45dc3ea..3a8e2d817 100644 --- a/engine/classes/ElggMetadata.php +++ b/engine/classes/ElggMetadata.php @@ -6,6 +6,10 @@   *   * @package    Elgg.Core   * @subpackage Metadata + * + * @property string $value_type + * @property int $owner_guid + * @property string $enabled   */  class ElggMetadata extends ElggExtender { diff --git a/engine/classes/ElggObject.php b/engine/classes/ElggObject.php index 6263f84f6..aeaa3ba5c 100644 --- a/engine/classes/ElggObject.php +++ b/engine/classes/ElggObject.php @@ -66,21 +66,18 @@ class ElggObject extends ElggEntity {  					$msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));  					throw new IOException($msg);  				} - -			// Is $guid is an ElggObject? Use a copy constructor  			} else if ($guid instanceof ElggObject) { +				// $guid is an ElggObject so this is a copy constructor  				elgg_deprecated_notice('This type of usage of the ElggObject constructor was deprecated. Please use the clone method.', 1.7);  				foreach ($guid->attributes as $key => $value) {  					$this->attributes[$key] = $value;  				} - -			// Is this is an ElggEntity but not an ElggObject = ERROR!  			} else if ($guid instanceof ElggEntity) { +				// @todo remove - do not need separate exception  				throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggObject')); - -			// Is it a GUID  			} else if (is_numeric($guid)) { +				// $guid is a GUID so load  				if (!$this->load($guid)) {  					throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));  				} @@ -110,7 +107,7 @@ class ElggObject extends ElggEntity {  		$this->attributes = $attrs;  		$this->attributes['tables_loaded'] = 2; -		cache_entity($this); +		_elgg_cache_entity($this);  		return true;  	} @@ -129,8 +126,12 @@ class ElggObject extends ElggEntity {  		}  		// Save ElggObject-specific attributes -		return create_object_entity($this->get('guid'), $this->get('title'), -			$this->get('description')); + +		_elgg_disable_caching_for_entity($this->guid); +		$ret = create_object_entity($this->get('guid'), $this->get('title'), $this->get('description')); +		_elgg_enable_caching_for_entity($this->guid); + +		return $ret;  	}  	/** diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php index 8f71b79a8..545b9a53c 100644 --- a/engine/classes/ElggPlugin.php +++ b/engine/classes/ElggPlugin.php @@ -145,7 +145,7 @@ class ElggPlugin extends ElggObject {  	/**  	 * Sets the location of this plugin.  	 * -	 * @param path $id The path to the plugin's dir. +	 * @param string $id The path to the plugin's dir.  	 * @return bool  	 */  	public function setID($id) { @@ -299,17 +299,15 @@ class ElggPlugin extends ElggObject {  		$private_settings = get_data($q); -		if ($private_settings) { -			$return = array(); +		$return = array(); +		if ($private_settings) {  			foreach ($private_settings as $setting) {  				$return[$setting->name] = $setting->value;  			} - -			return $return;  		} -		return false; +		return $return;  	}  	/** @@ -350,11 +348,14 @@ class ElggPlugin extends ElggObject {  	 */  	public function unsetAllSettings() {  		$db_prefix = get_config('dbprefix'); -		$ps_prefix = elgg_namespace_plugin_private_setting('setting', ''); + +		$us_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID()); +		$is_prefix = elgg_namespace_plugin_private_setting('internal', '', $this->getID());  		$q = "DELETE FROM {$db_prefix}private_settings  			WHERE entity_guid = $this->guid -			AND name NOT LIKE '$ps_prefix%'"; +			AND name NOT LIKE '$us_prefix%' +			AND name NOT LIKE '$is_prefix%'";  		return delete_data($q);  	} @@ -420,20 +421,18 @@ class ElggPlugin extends ElggObject {  		$private_settings = get_data($q); -		if ($private_settings) { -			$return = array(); +		$return = array(); +		if ($private_settings) {  			foreach ($private_settings as $setting) {  				$name = substr($setting->name, $ps_prefix_len);  				$value = $setting->value;  				$return[$name] = $value;  			} - -			return $return;  		} -		return false; +		return $return;  	}  	/** @@ -546,7 +545,7 @@ class ElggPlugin extends ElggObject {  	 * Returns if the plugin is complete, meaning has all required files  	 * and Elgg can read them and they make sense.  	 * -	 * @todo bad name? This could be confused with isValid() from ElggPackage. +	 * @todo bad name? This could be confused with isValid() from ElggPluginPackage.  	 *  	 * @return bool  	 */ @@ -597,6 +596,8 @@ class ElggPlugin extends ElggObject {  	 * Checks if this plugin can be activated on the current  	 * Elgg installation.  	 * +	 * @todo remove $site_guid param or implement it +	 *  	 * @param mixed $site_guid Optional site guid  	 * @return bool  	 */ @@ -647,8 +648,8 @@ class ElggPlugin extends ElggObject {  			// Note: this will not run re-run the init hooks!  			if ($return) {  				if ($this->canReadFile('activate.php')) { -					$flags = ELGG_PLUGIN_INCLUDE_START | ELGG_PLUGIN_REGISTER_CLASSES -							| ELGG_PLUGIN_REGISTER_LANGUAGES | ELGG_PLUGIN_REGISTER_VIEWS; +					$flags = ELGG_PLUGIN_INCLUDE_START | ELGG_PLUGIN_REGISTER_CLASSES | +							ELGG_PLUGIN_REGISTER_LANGUAGES | ELGG_PLUGIN_REGISTER_VIEWS;  					$this->start($flags); diff --git a/engine/classes/ElggPluginPackage.php b/engine/classes/ElggPluginPackage.php index 2dc4bdb3d..37eb4bf4d 100644 --- a/engine/classes/ElggPluginPackage.php +++ b/engine/classes/ElggPluginPackage.php @@ -100,7 +100,6 @@ class ElggPluginPackage {  	 * @param string $plugin   The ID (directory name) or full path of the plugin.  	 * @param bool   $validate Automatically run isValid()?  	 * -	 * @return true  	 * @throws PluginException  	 */  	public function __construct($plugin, $validate = true) { @@ -213,6 +212,7 @@ class ElggPluginPackage {  			return false;  		} +		// Note: $conflicts and $requires are not unused. They're called dynamically  		$conflicts = $this->getManifest()->getConflicts();  		$requires = $this->getManifest()->getRequires();  		$provides = $this->getManifest()->getProvides(); @@ -294,6 +294,7 @@ class ElggPluginPackage {  			return true;  		} +		$this->errorMsg = elgg_echo('unknown_error');  		return false;  	} @@ -330,8 +331,10 @@ class ElggPluginPackage {  	 * @return bool|array  	 */  	public function checkDependencies($full_report = false) { +		// Note: $conflicts and $requires are not unused. They're called dynamically  		$requires = $this->getManifest()->getRequires();  		$conflicts = $this->getManifest()->getConflicts(); +  		$enabled_plugins = elgg_get_plugins('active');  		$this_id = $this->getID();  		$report = array(); @@ -368,6 +371,7 @@ class ElggPluginPackage {  		$check_types = array('requires', 'conflicts');  		if ($full_report) { +			// Note: $suggests is not unused. It's called dynamically  			$suggests = $this->getManifest()->getSuggests();  			$check_types[] = 'suggests';  		} diff --git a/engine/classes/ElggPriorityList.php b/engine/classes/ElggPriorityList.php index 8a3b836a8..416df885c 100644 --- a/engine/classes/ElggPriorityList.php +++ b/engine/classes/ElggPriorityList.php @@ -89,7 +89,7 @@   *	return true;   * }   * - * @package Elgg.Core + * @package    Elgg.Core   * @subpackage Helpers   */  class ElggPriorityList @@ -126,7 +126,9 @@ class ElggPriorityList  	 *                        maintains its priority and the new element is to the next available  	 *                        slot, taking into consideration all previously registered elements.  	 *                        Negative elements are accepted. +	 * @param bool  $exact    unused  	 * @return int            The priority of the added element. +	 * @todo remove $exact or implement it. Note we use variable name strict below.  	 */  	public function add($element, $priority = null, $exact = false) {  		if ($priority !== null && !is_numeric($priority)) { @@ -146,7 +148,8 @@ class ElggPriorityList  	 * @warning The element must have the same attributes / values. If using $strict, it must have  	 *          the same types. array(10) will fail in strict against array('10') (str vs int).  	 * -	 * @param type $element +	 * @param mixed $element The element to remove from the list +	 * @param bool  $strict  Whether to check the type of the element match  	 * @return bool  	 */  	public function remove($element, $strict = false) { @@ -162,10 +165,10 @@ class ElggPriorityList  	/**  	 * Move an existing element to a new priority.  	 * -	 * @param mixed  $current_priority -	 * @param int    $new_priority -	 * -	 * @return int The new priority. +	 * @param mixed $element      The element to move +	 * @param int   $new_priority The new priority for the element +	 * @param bool  $strict       Whether to check the type of the element match +	 * @return bool  	 */  	public function move($element, $new_priority, $strict = false) {  		$new_priority = (int) $new_priority; @@ -200,12 +203,12 @@ class ElggPriorityList  	 *  	 * If no user function is provided the elements are sorted by priority registered.  	 * -	 * The callback function should accept the array of elements as the first argument and should -	 * return a sorted array. +	 * The callback function should accept the array of elements as the first  +	 * argument and should return a sorted array.  	 *  	 * This function can be called multiple times.  	 * -	 * @param type $callback +	 * @param callback $callback The callback for sorting. Numeric sorting is the default.  	 * @return bool  	 */  	public function sort($callback = null) { @@ -268,7 +271,7 @@ class ElggPriorityList  	/**  	 * Returns the element at $priority.  	 * -	 * @param int $priority +	 * @param int $priority The priority  	 * @return mixed The element or false on fail.  	 */  	public function getElement($priority) { @@ -351,7 +354,12 @@ class ElggPriorityList  		return ($key !== NULL && $key !== FALSE);  	} -	// Countable +	/** +	 * Countable interface +	 * +	 * @see Countable::count() +	 * @return int +	 */  	public function count() {  		return count($this->elements);  	} diff --git a/engine/classes/ElggRelationship.php b/engine/classes/ElggRelationship.php index efc0f7eff..d2e88882a 100644 --- a/engine/classes/ElggRelationship.php +++ b/engine/classes/ElggRelationship.php @@ -71,6 +71,7 @@ class ElggRelationship extends ElggData implements  	 * Save the relationship  	 *  	 * @return int the relationship id +	 * @throws IOException  	 */  	public function save() {  		if ($this->id > 0) { @@ -145,7 +146,7 @@ class ElggRelationship extends ElggData implements  	 * @param ODD $data ODD data  	 * @return bool -	 * @throws ImportException +	 * @throws ImportException|InvalidParameterException  	 */  	public function import(ODD $data) {  		if (!($data instanceof ODDRelationship)) { @@ -179,6 +180,8 @@ class ElggRelationship extends ElggData implements  				return true;  			}  		} + +		return false;  	}  	// SYSTEM LOG INTERFACE //////////////////////////////////////////////////////////// diff --git a/engine/classes/ElggSite.php b/engine/classes/ElggSite.php index 1fe49b85c..dd996fe98 100644 --- a/engine/classes/ElggSite.php +++ b/engine/classes/ElggSite.php @@ -77,28 +77,24 @@ class ElggSite extends ElggEntity {  					$msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));  					throw new IOException($msg);  				} - -			// Is $guid is an ElggSite? Use a copy constructor  			} else if ($guid instanceof ElggSite) { +				// $guid is an ElggSite so this is a copy constructor  				elgg_deprecated_notice('This type of usage of the ElggSite constructor was deprecated. Please use the clone method.', 1.7);  				foreach ($guid->attributes as $key => $value) {  					$this->attributes[$key] = $value;  				} - -			// Is this is an ElggEntity but not an ElggSite = ERROR!  			} else if ($guid instanceof ElggEntity) { +				// @todo remove and just use else clause  				throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggSite')); - -			// See if this is a URL  			} else if (strpos($guid, "http") !== false) { +				// url so retrieve by url  				$guid = get_site_by_url($guid);  				foreach ($guid->attributes as $key => $value) {  					$this->attributes[$key] = $value;  				} - -			// Is it a GUID  			} else if (is_numeric($guid)) { +				// $guid is a GUID so load  				if (!$this->load($guid)) {  					throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));  				} @@ -128,7 +124,7 @@ class ElggSite extends ElggEntity {  		$this->attributes = $attrs;  		$this->attributes['tables_loaded'] = 2; -		cache_entity($this); +		_elgg_cache_entity($this);  		return true;  	} @@ -381,7 +377,9 @@ class ElggSite extends ElggEntity {  				elgg_register_plugin_hook_handler('index', 'system', 'elgg_walled_garden_index', 1);  				if (!$this->isPublicPage()) { -					$_SESSION['last_forward_from'] = current_page_url(); +					if (!elgg_is_xhr()) { +						$_SESSION['last_forward_from'] = current_page_url(); +					}  					register_error(elgg_echo('loggedinrequired'));  					forward();  				} @@ -443,8 +441,6 @@ class ElggSite extends ElggEntity {  		// include a hook for plugin authors to include public pages  		$plugins = elgg_trigger_plugin_hook('public_pages', 'walled_garden', NULL, array()); -		// lookup admin-specific public pages -  		// allow public pages  		foreach (array_merge($defaults, $plugins) as $public) {  			$pattern = "`^{$CONFIG->url}$public/*$`i"; diff --git a/engine/classes/ElggStaticVariableCache.php b/engine/classes/ElggStaticVariableCache.php index 17d849400..9c14fdfba 100644 --- a/engine/classes/ElggStaticVariableCache.php +++ b/engine/classes/ElggStaticVariableCache.php @@ -11,7 +11,7 @@ class ElggStaticVariableCache extends ElggSharedMemoryCache {  	/**  	 * The cache.  	 * -	 * @var unknown_type +	 * @var array  	 */  	private static $__cache; @@ -22,7 +22,7 @@ class ElggStaticVariableCache extends ElggSharedMemoryCache {  	 * memory, optionally with a given namespace (to avoid overlap).  	 *  	 * @param string $namespace The namespace for this cache to write to. -	 * @note namespaces of the same name are shared! +	 * @warning namespaces of the same name are shared!  	 */  	function __construct($namespace = 'default') {  		$this->setNamespace($namespace); @@ -80,7 +80,7 @@ class ElggStaticVariableCache extends ElggSharedMemoryCache {  	}  	/** -	 * This was probably meant to delete everything? +	 * Clears the cache for a particular namespace  	 *  	 * @return void  	 */ diff --git a/engine/classes/ElggTranslit.php b/engine/classes/ElggTranslit.php index 676c59fc8..b4bf87797 100644 --- a/engine/classes/ElggTranslit.php +++ b/engine/classes/ElggTranslit.php @@ -20,11 +20,10 @@   * and is licensed under the LGPL. For more information, see   * <http://www.doctrine-project.org>.   * - * @author      Konsta Vesterinen <kvesteri@cc.hut.fi> - * @author      Jonathan H. Wage <jonwage@gmail.com> - * - * @author      Steve Clay <steve@mrclay.org> - * @package     Elgg.Core + * @package Elgg.Core + * @author  Konsta Vesterinen <kvesteri@cc.hut.fi> + * @author  Jonathan H. Wage <jonwage@gmail.com> + * @author  Steve Clay <steve@mrclay.org>   *   * @access private Plugin authors should not use this directly   */ @@ -32,8 +31,9 @@ class ElggTranslit {  	/**  	 * Create a version of a string for embedding in a URL -	 * @param string $string a UTF-8 string -	 * @param string $separator +	 * +	 * @param string $string    A UTF-8 string +	 * @param string $separator The character to separate words with  	 * @return string  	 */  	static public function urlize($string, $separator = '-') { @@ -49,24 +49,29 @@ class ElggTranslit {  		// Internationalization, AND 日本語!  		$string = self::transliterateAscii($string); -		// more translation +		// allow HTML tags in titles +		$string = preg_replace('~<([a-zA-Z][^>]*)>~', ' $1 ', $string); + +		// more substitutions +		// @todo put these somewhere else  		$string = strtr($string, array( -			// Euro/GBP -			"\xE2\x82\xAC" /* € */ => 'E', "\xC2\xA3" /* £ */ => 'GBP', +			// currency +			"\xE2\x82\xAC" /* € */ => ' E ', +			"\xC2\xA3" /* £ */ => ' GBP ',  		));  		// remove all ASCII except 0-9a-zA-Z, hyphen, underscore, and whitespace  		// note: "x" modifier did not work with this pattern.  		$string = preg_replace('~[' -			. '\x00-\x08'  # control chars -			. '\x0b\x0c'   # vert tab, form feed -			. '\x0e-\x1f'  # control chars -			. '\x21-\x2c'  # ! ... , -			. '\x2e\x2f'   # . slash -			. '\x3a-\x40'  # : ... @ -			. '\x5b-\x5e'  # [ ... ^ -			. '\x60'       # ` -			. '\x7b-\x7f'  # { ... DEL +			. '\x00-\x08'  // control chars +			. '\x0b\x0c'   // vert tab, form feed +			. '\x0e-\x1f'  // control chars +			. '\x21-\x2c'  // ! ... , +			. '\x2e\x2f'   // . slash +			. '\x3a-\x40'  // : ... @ +			. '\x5b-\x5e'  // [ ... ^ +			. '\x60'       // ` +			. '\x7b-\x7f'  // { ... DEL  			. ']~', '', $string);  		$string = strtr($string, '', ''); @@ -80,10 +85,10 @@ class ElggTranslit {  		// note: we cannot use [^0-9a-zA-Z] because that matches multibyte chars.  		// note: "x" modifier did not work with this pattern.  		$pattern = '~[' -			. '\x00-\x2f'  # controls ... slash -			. '\x3a-\x40'  # : ... @ -			. '\x5b-\x60'  # [ ... ` -			. '\x7b-\x7f'  # { ... DEL +			. '\x00-\x2f'  // controls ... slash +			. '\x3a-\x40'  // : ... @ +			. '\x5b-\x60'  // [ ... ` +			. '\x7b-\x7f'  // { ... DEL  			. ']+~x';  		// ['internationalization', 'and', '日本語'] @@ -98,6 +103,7 @@ class ElggTranslit {  	/**  	 * Transliterate Western multibyte chars to ASCII +	 *  	 * @param string $utf8 a UTF-8 string  	 * @return string  	 */ @@ -247,6 +253,7 @@ class ElggTranslit {  	/**  	 * Tests that "normalizer_normalize" exists and works +	 *  	 * @return bool  	 */  	static public function hasNormalizerSupport() { @@ -255,7 +262,7 @@ class ElggTranslit {  			$form_c = "\xC3\x85"; // 'LATIN CAPITAL LETTER A WITH RING ABOVE' (U+00C5)  			$form_d = "A\xCC\x8A"; // A followed by 'COMBINING RING ABOVE' (U+030A)  			$ret = (function_exists('normalizer_normalize') -				    && $form_c === normalizer_normalize($form_d)); +				&& $form_c === normalizer_normalize($form_d));  		}  		return $ret;  	} diff --git a/engine/classes/ElggUser.php b/engine/classes/ElggUser.php index 6c1cdc1de..6163f9b62 100644 --- a/engine/classes/ElggUser.php +++ b/engine/classes/ElggUser.php @@ -40,6 +40,9 @@ class ElggUser extends ElggEntity  		$this->attributes['code'] = NULL;  		$this->attributes['banned'] = "no";  		$this->attributes['admin'] = 'no'; +		$this->attributes['prev_last_action'] = NULL; +		$this->attributes['last_login'] = NULL; +		$this->attributes['prev_last_login'] = NULL;  		$this->attributes['tables_split'] = 2;  	} @@ -65,30 +68,26 @@ class ElggUser extends ElggEntity  					$msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));  					throw new IOException($msg);  				} - -			// See if this is a username  			} else if (is_string($guid)) { +				// $guid is a username  				$user = get_user_by_username($guid);  				if ($user) {  					foreach ($user->attributes as $key => $value) {  						$this->attributes[$key] = $value;  					}  				} - -			// Is $guid is an ElggUser? Use a copy constructor  			} else if ($guid instanceof ElggUser) { +				// $guid is an ElggUser so this is a copy constructor  				elgg_deprecated_notice('This type of usage of the ElggUser constructor was deprecated. Please use the clone method.', 1.7);  				foreach ($guid->attributes as $key => $value) {  					$this->attributes[$key] = $value;  				} - -			// Is this is an ElggEntity but not an ElggUser = ERROR!  			} else if ($guid instanceof ElggEntity) { +				// @todo why have a special case here  				throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggUser')); - -			// Is it a GUID  			} else if (is_numeric($guid)) { +				// $guid is a GUID so load entity  				if (!$this->load($guid)) {  					throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));  				} @@ -116,7 +115,7 @@ class ElggUser extends ElggEntity  		$this->attributes = $attrs;  		$this->attributes['tables_loaded'] = 2; -		cache_entity($this); +		_elgg_cache_entity($this);  		return true;  	} @@ -133,9 +132,13 @@ class ElggUser extends ElggEntity  		}  		// Now save specific stuff -		return create_user_entity($this->get('guid'), $this->get('name'), $this->get('username'), +		_elgg_disable_caching_for_entity($this->guid); +		$ret = create_user_entity($this->get('guid'), $this->get('name'), $this->get('username'),  			$this->get('password'), $this->get('salt'), $this->get('email'), $this->get('language'),  			$this->get('code')); +		_elgg_enable_caching_for_entity($this->guid); + +		return $ret;  	}  	/** diff --git a/engine/classes/ElggVolatileMetadataCache.php b/engine/classes/ElggVolatileMetadataCache.php index 8a33c198d..4acda7cee 100644 --- a/engine/classes/ElggVolatileMetadataCache.php +++ b/engine/classes/ElggVolatileMetadataCache.php @@ -33,9 +33,11 @@ class ElggVolatileMetadataCache {  	protected $ignoreAccess = null;  	/** -	 * @param int $entity_guid -	 * -	 * @param array $values +	 * Cache metadata for an entity +	 *  +	 * @param int   $entity_guid The GUID of the entity +	 * @param array $values      The metadata values to cache +	 * @return void  	 */  	public function saveAll($entity_guid, array $values) {  		if (!$this->getIgnoreAccess()) { @@ -45,8 +47,9 @@ class ElggVolatileMetadataCache {  	}  	/** -	 * @param int $entity_guid -	 * +	 * Get the metadata for an entity +	 *  +	 * @param int $entity_guid The GUID of the entity  	 * @return array  	 */  	public function loadAll($entity_guid) { @@ -61,15 +64,17 @@ class ElggVolatileMetadataCache {  	 * Declare that there may be fetch-able metadata names in storage that this  	 * cache doesn't know about  	 * -	 * @param int $entity_guid +	 * @param int $entity_guid The GUID of the entity +	 * @return void  	 */  	public function markOutOfSync($entity_guid) {  		unset($this->isSynchronized[$entity_guid]);  	}  	/** -	 * @param $entity_guid -	 * +	 * Have all the metadata for this entity been cached? +	 *  +	 * @param int $entity_guid The GUID of the entity  	 * @return bool  	 */  	public function isSynchronized($entity_guid) { @@ -77,13 +82,15 @@ class ElggVolatileMetadataCache {  	}  	/** -	 * @param int $entity_guid -	 * -	 * @param string $name -	 * -	 * @param array|int|string|null $value  null means it is known that there is no -	 *                                      fetch-able metadata under this name -	 * @param bool $allow_multiple +	 * Cache a piece of metadata +	 *  +	 * @param int                   $entity_guid    The GUID of the entity +	 * @param string                $name           The metadata name +	 * @param array|int|string|null $value          The metadata value. null means it is  +	 *                                              known that there is no fetch-able  +	 *                                              metadata under this name +	 * @param bool                  $allow_multiple Can the metadata be an array +	 * @return void  	 */  	public function save($entity_guid, $name, $value, $allow_multiple = false) {  		if ($this->getIgnoreAccess()) { @@ -115,10 +122,8 @@ class ElggVolatileMetadataCache {  	 * function's return value should be trusted (otherwise a null return value  	 * is ambiguous).  	 * -	 * @param int $entity_guid -	 * -	 * @param string $name -	 * +	 * @param int    $entity_guid The GUID of the entity +	 * @param string $name        The metadata name  	 * @return array|string|int|null null = value does not exist  	 */  	public function load($entity_guid, $name) { @@ -133,9 +138,9 @@ class ElggVolatileMetadataCache {  	 * Forget about this metadata entry. We don't want to try to guess what the  	 * next fetch from storage will return  	 * -	 * @param int $entity_guid -	 * -	 * @param string $name +	 * @param int    $entity_guid The GUID of the entity +	 * @param string $name        The metadata name +	 * @return void  	 */  	public function markUnknown($entity_guid, $name) {  		unset($this->values[$entity_guid][$name]); @@ -145,10 +150,8 @@ class ElggVolatileMetadataCache {  	/**  	 * If true, load() will return an accurate value for this name  	 * -	 * @param int $entity_guid -	 * -	 * @param string $name -	 * +	 * @param int    $entity_guid The GUID of the entity +	 * @param string $name        The metadata name  	 * @return bool  	 */  	public function isKnown($entity_guid, $name) { @@ -163,10 +166,8 @@ class ElggVolatileMetadataCache {  	/**  	 * Declare that metadata under this name is known to be not fetch-able from storage  	 * -	 * @param int $entity_guid -	 * -	 * @param string $name -	 * +	 * @param int    $entity_guid The GUID of the entity +	 * @param string $name        The metadata name  	 * @return array  	 */  	public function markEmpty($entity_guid, $name) { @@ -176,7 +177,8 @@ class ElggVolatileMetadataCache {  	/**  	 * Forget about all metadata for an entity  	 * -	 * @param int $entity_guid +	 * @param int $entity_guid The GUID of the entity +	 * @return void  	 */  	public function clear($entity_guid) {  		$this->values[$entity_guid] = array(); @@ -185,6 +187,8 @@ class ElggVolatileMetadataCache {  	/**  	 * Clear entire cache and mark all entities as out of sync +	 *  +	 * @return void  	 */  	public function flush() {  		$this->values = array(); @@ -197,7 +201,8 @@ class ElggVolatileMetadataCache {  	 *  	 * This setting makes this component a little more loosely-coupled.  	 * -	 * @param bool $ignore +	 * @param bool $ignore Whether to ignore access or not +	 * @return void  	 */  	public function setIgnoreAccess($ignore) {  		$this->ignoreAccess = (bool) $ignore; @@ -205,12 +210,16 @@ class ElggVolatileMetadataCache {  	/**  	 * Tell the cache to call elgg_get_ignore_access() to determing access status. +	 *  +	 * @return void  	 */  	public function unsetIgnoreAccess() {  		$this->ignoreAccess = null;  	}  	/** +	 * Get the ignore access value +	 *   	 * @return bool  	 */  	protected function getIgnoreAccess() { @@ -225,12 +234,10 @@ class ElggVolatileMetadataCache {  	 * Invalidate based on options passed to the global *_metadata functions  	 *  	 * @param string $action  Action performed on metadata. "delete", "disable", or "enable" -	 * -	 * @param array $options  Options passed to elgg_(delete|disable|enable)_metadata -	 * -	 *   "guid" if given, invalidation will be limited to this entity -	 * -	 *   "metadata_name" if given, invalidation will be limited to metadata with this name +	 * @param array  $options Options passed to elgg_(delete|disable|enable)_metadata +	 *                         "guid" if given, invalidation will be limited to this entity +	 *                         "metadata_name" if given, invalidation will be limited to metadata with this name +	 * @return void  	 */  	public function invalidateByOptions($action, array $options) {  		// remove as little as possible, optimizing for common cases @@ -254,7 +261,10 @@ class ElggVolatileMetadataCache {  	}  	/** -	 * @param int|array $guids +	 * Populate the cache from a set of entities +	 *  +	 * @param int|array $guids Array of or single GUIDs +	 * @return void  	 */  	public function populateFromEntities($guids) {  		if (empty($guids)) { @@ -318,9 +328,7 @@ class ElggVolatileMetadataCache {  	 * cache if RAM usage becomes an issue.  	 *  	 * @param array $guids GUIDs of entities to examine -	 * -	 * @param int $limit Limit in characters of all metadata (with ints casted to strings) -	 * +	 * @param int   $limit Limit in characters of all metadata (with ints casted to strings)  	 * @return array  	 */  	public function filterMetadataHeavyEntities(array $guids, $limit = 1024000) { diff --git a/engine/classes/ElggWidget.php b/engine/classes/ElggWidget.php index 99708f66a..66191bf47 100644 --- a/engine/classes/ElggWidget.php +++ b/engine/classes/ElggWidget.php @@ -7,6 +7,11 @@   *   * @package    Elgg.Core   * @subpackage Widgets + * + * @property-read string $handler internal, do not use + * @property-read string $column internal, do not use + * @property-read string $order internal, do not use + * @property-read string $context internal, do not use   */  class ElggWidget extends ElggObject { @@ -141,10 +146,15 @@ class ElggWidget extends ElggObject {  			}  		} +		$bottom_rank = count($widgets); +		if ($column == $this->column) { +			$bottom_rank--; +		} +		  		if ($rank == 0) {  			// top of the column  			$this->order = reset($widgets)->order - 10; -		} elseif ($rank == (count($widgets) - 1)) { +		} elseif ($rank == $bottom_rank) {  			// bottom of the column of active widgets  			$this->order = end($widgets)->order + 10;  		} else { diff --git a/engine/classes/ElggXMLElement.php b/engine/classes/ElggXMLElement.php index 65a13912c..cbd3fc5ce 100644 --- a/engine/classes/ElggXMLElement.php +++ b/engine/classes/ElggXMLElement.php @@ -20,7 +20,12 @@ class ElggXMLElement {  		if ($xml instanceof SimpleXMLElement) {  			$this->_element = $xml;  		} else { +			// do not load entities +			$disable_load_entities = libxml_disable_entity_loader(true); +  			$this->_element = new SimpleXMLElement($xml); + +			libxml_disable_entity_loader($disable_load_entities);  		}  	} @@ -32,7 +37,7 @@ class ElggXMLElement {  	}  	/** -	 * @return array:string The attributes +	 * @return string[] The attributes  	 */  	public function getAttributes() {  		//include namespace declarations as attributes @@ -64,7 +69,7 @@ class ElggXMLElement {  	}  	/** -	 * @return array:ElggXMLElement Child elements +	 * @return ElggXMLElement[] Child elements  	 */  	public function getChildren() {  		$children = $this->_element->children(); @@ -76,6 +81,12 @@ class ElggXMLElement {  		return $result;  	} +	/** +	 * Override -> +	 *  +	 * @param string $name Property name +	 * @return mixed +	 */  	function __get($name) {  		switch ($name) {  			case 'name': @@ -94,6 +105,12 @@ class ElggXMLElement {  		return null;  	} +	/** +	 * Override isset +	 *  +	 * @param string $name Property name +	 * @return boolean +	 */  	function __isset($name) {  		switch ($name) {  			case 'name': @@ -111,5 +128,4 @@ class ElggXMLElement {  		}  		return false;  	} - -}
\ No newline at end of file +} diff --git a/engine/classes/ODDMetaData.php b/engine/classes/ODDMetaData.php index 58862e0fb..09b653582 100644 --- a/engine/classes/ODDMetaData.php +++ b/engine/classes/ODDMetaData.php @@ -10,12 +10,12 @@ class ODDMetaData extends ODD {  	/**  	 * New ODD metadata  	 * -	 * @param unknown_type $uuid        Unique ID -	 * @param unknown_type $entity_uuid Another unique ID -	 * @param unknown_type $name        Name -	 * @param unknown_type $value       Value -	 * @param unknown_type $type        Type -	 * @param unknown_type $owner_uuid  Owner ID +	 * @param string $uuid        Unique ID +	 * @param string $entity_uuid Another unique ID +	 * @param string $name        Name +	 * @param string $value       Value +	 * @param string $type        Type +	 * @param string $owner_uuid  Owner ID  	 */  	function __construct($uuid, $entity_uuid, $name, $value, $type = "", $owner_uuid = "") {  		parent::__construct(); @@ -31,7 +31,7 @@ class ODDMetaData extends ODD {  	/**  	 * Returns 'metadata'  	 * -	 * @return 'metadata' +	 * @return string 'metadata'  	 */  	protected function getTagName() {  		return "metadata"; diff --git a/engine/classes/ODDRelationship.php b/engine/classes/ODDRelationship.php index 2906b1c73..8b1fe217b 100644 --- a/engine/classes/ODDRelationship.php +++ b/engine/classes/ODDRelationship.php @@ -10,9 +10,9 @@ class ODDRelationship extends ODD {  	/**  	 * New ODD Relationship  	 * -	 * @param unknown_type $uuid1 First UUID -	 * @param unknown_type $type  Type of telationship -	 * @param unknown_type $uuid2 Second UUId +	 * @param string $uuid1 First UUID +	 * @param string $type  Type of telationship +	 * @param string $uuid2 Second UUId  	 */  	function __construct($uuid1, $type, $uuid2) {  		parent::__construct(); @@ -25,7 +25,7 @@ class ODDRelationship extends ODD {  	/**  	 * Returns 'relationship'  	 * -	 * @return 'relationship' +	 * @return string 'relationship'  	 */  	protected function getTagName() {  		return "relationship";  | 
