You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1017 lines
						
					
					
						
							31 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							1017 lines
						
					
					
						
							31 KiB
						
					
					
				| <?php | |
| 
 | |
| /* | |
|  | |
| 	Copyright (c) 2009-2019 F3::Factory/Bong Cosca, All rights reserved. | |
|  | |
| 	This file is part of the Fat-Free Framework (http://fatfreeframework.com). | |
|  | |
| 	This is free software: you can redistribute it and/or modify it under the | |
| 	terms of the GNU General Public License as published by the Free Software | |
| 	Foundation, either version 3 of the License, or later. | |
|  | |
| 	Fat-Free Framework is distributed in the hope that it will be useful, | |
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | |
| 	General Public License for more details. | |
|  | |
| 	You should have received a copy of the GNU General Public License along | |
| 	with Fat-Free Framework.  If not, see <http://www.gnu.org/licenses/>. | |
|  | |
| */ | |
| 
 | |
| //! Wrapper for various HTTP utilities | |
| class Web extends Prefab { | |
| 
 | |
| 	//@{ Error messages | |
| 	const | |
| 		E_Request='No suitable HTTP request engine found'; | |
| 	//@} | |
|  | |
| 	protected | |
| 		//! HTTP request engine | |
| 		$wrapper; | |
| 
 | |
| 	/** | |
| 	*	Detect MIME type using file extension or file inspection | |
| 	*	@return string | |
| 	*	@param $file string | |
| 	*	@param $inspect bool | |
| 	**/ | |
| 	function mime($file, $inspect=FALSE) { | |
| 		if ($inspect) { | |
| 			if (is_file($file) && is_readable($file)) { | |
| 				// physical files | |
| 				if (extension_loaded('fileinfo')) | |
| 					$mime=mime_content_type($file); | |
| 				elseif (preg_match('/Darwin/i',PHP_OS)) | |
| 					$mime=trim(exec('file -bI '.escapeshellarg($file))); | |
| 				elseif (!preg_match('/^win/i',PHP_OS)) | |
| 					$mime=trim(exec('file -bi '.escapeshellarg($file))); | |
| 				if (isset($mime) && !empty($mime)){ | |
| 					// cut charset information if any | |
| 					$exp=explode(';',$mime,2); | |
| 					$mime=$exp[0]; | |
| 				} | |
| 			} | |
| 			else { | |
| 				// remote and stream files | |
| 				if (ini_get('allow_url_fopen') && ($fhandle=fopen($file,'rb'))) { | |
| 					// only get head bytes instead of whole file | |
| 					$bytes=fread($fhandle,20); | |
| 					fclose($fhandle); | |
| 				} | |
| 				elseif (($response=$this->request($file,['method' => 'HEAD'])) | |
| 					&& preg_grep('/HTTP\/[\d.]{1,3} 200/',$response['headers']) | |
| 					&& ($type = preg_grep('/^Content-Type:/i',$response['headers']))) { | |
| 					// get mime type directly from response header | |
| 					return preg_replace('/^Content-Type:\s*/i','',array_pop($type)); | |
| 				} | |
| 				else // load whole file | |
| 					$bytes=file_get_contents($file); | |
| 				if (extension_loaded('fileinfo')) { | |
| 					// get mime from fileinfo | |
| 					$finfo=finfo_open(FILEINFO_MIME_TYPE); | |
| 					$mime=finfo_buffer($finfo,$bytes); | |
| 				} | |
| 				elseif ($bytes) { | |
| 					// magic number header fallback | |
| 					$map=[ | |
| 						'\x64\x6E\x73\x2E'=>'audio/basic', | |
| 						'\x52\x49\x46\x46.{4}\x41\x56\x49\x20\x4C\x49\x53\x54'=>'video/avi', | |
| 						'\x42\x4d'=>'image/bmp', | |
| 						'\x42\x5A\x68'=>'application/x-bzip2', | |
| 						'\x07\x64\x74\x32\x64\x64\x74\x64'=>'application/xml-dtd', | |
| 						'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1'=>'application/msword', | |
| 						'\x50\x4B\x03\x04\x14\x00\x06\x00'=>'application/msword', | |
| 						'\x0D\x44\x4F\x43'=>'application/msword', | |
| 						'GIF\d+a'=>'image/gif', | |
| 						'\x1F\x8B'=>'application/x-gzip', | |
| 						'\xff\xd8\xff'=>'image/jpeg', | |
| 						'\x49\x46\x00'=>'image/jpeg', | |
| 						'\xFF\xFB'=>'audio/mpeg', | |
| 						'\x49\x44\x33'=>'audio/mpeg', | |
| 						'\x00\x00\x01\xBA'=>'video/mpeg', | |
| 						'\x4F\x67\x67\x53\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00'=>'audio/vorbis', | |
| 						'\x25\x50\x44\x46'=>'application/pdf', | |
| 						'\x89PNG\x0d\x0a'=>'image/png', | |
| 						'.{4}\x6D\x6F\x6F\x76\x'=>'video/quicktime', | |
| 						'\x53\x49\x54\x21\x00'=>'application/x-stuffit', | |
| 						'\x43\x57\x53'=>'application/x-shockwave-flash', | |
| 						'\x1F\x8B\x08'=>'application/x-tar', | |
| 						'\x49\x20\x49'=>'image/tiff', | |
| 						'\x52\x49\x46\x46.{4}\x57\x41\x56\x45\x66\x6D\x74\x20'=>'audio/wav', | |
| 						'\xFD\xFF\xFF\xFF\x20\x00\x00\x00'=>'application/vnd.ms-excel', | |
| 						'\x50\x4B\x03\x04'=>'application/x-zip-compressed', | |
| 						'[ -~]+$'=>'text/plain', | |
| 					]; | |
| 					foreach ($map as $key=>$val) | |
| 						if (preg_match('/^'.$key.'/',substr($bytes,0,128))) | |
| 							return $val; | |
| 				} | |
| 			} | |
| 			if (isset($mime) && !empty($mime)) | |
| 				return $mime; | |
| 			// Fallback to file extension-based check if no mime was found yet | |
| 		} | |
| 		if (preg_match('/\w+$/',$file,$ext)) { | |
| 			$map=[ | |
| 				'au'=>'audio/basic', | |
| 				'avi'=>'video/avi', | |
| 				'bmp'=>'image/bmp', | |
| 				'bz2'=>'application/x-bzip2', | |
| 				'css'=>'text/css', | |
| 				'dtd'=>'application/xml-dtd', | |
| 				'doc'=>'application/msword', | |
| 				'gif'=>'image/gif', | |
| 				'gz'=>'application/x-gzip', | |
| 				'hqx'=>'application/mac-binhex40', | |
| 				'html?'=>'text/html', | |
| 				'jar'=>'application/java-archive', | |
| 				'jpe?g|jfif?'=>'image/jpeg', | |
| 				'js'=>'application/x-javascript', | |
| 				'midi'=>'audio/x-midi', | |
| 				'mp3'=>'audio/mpeg', | |
| 				'mpe?g'=>'video/mpeg', | |
| 				'ogg'=>'audio/vorbis', | |
| 				'pdf'=>'application/pdf', | |
| 				'png'=>'image/png', | |
| 				'ppt'=>'application/vnd.ms-powerpoint', | |
| 				'ps'=>'application/postscript', | |
| 				'qt'=>'video/quicktime', | |
| 				'ram?'=>'audio/x-pn-realaudio', | |
| 				'rdf'=>'application/rdf', | |
| 				'rtf'=>'application/rtf', | |
| 				'sgml?'=>'text/sgml', | |
| 				'sit'=>'application/x-stuffit', | |
| 				'svg'=>'image/svg+xml', | |
| 				'swf'=>'application/x-shockwave-flash', | |
| 				'tgz'=>'application/x-tar', | |
| 				'tiff'=>'image/tiff', | |
| 				'txt'=>'text/plain', | |
| 				'wav'=>'audio/wav', | |
| 				'xls'=>'application/vnd.ms-excel', | |
| 				'xml'=>'application/xml', | |
| 				'zip'=>'application/x-zip-compressed' | |
| 			]; | |
| 			foreach ($map as $key=>$val) | |
| 				if (preg_match('/'.$key.'/',strtolower($ext[0]))) | |
| 					return $val; | |
| 		} | |
| 		return 'application/octet-stream'; | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Return the MIME types stated in the HTTP Accept header as an array; | |
| 	*	If a list of MIME types is specified, return the best match; or | |
| 	*	FALSE if none found | |
| 	*	@return array|string|FALSE | |
| 	*	@param $list string|array | |
| 	**/ | |
| 	function acceptable($list=NULL) { | |
| 		$accept=[]; | |
| 		foreach (explode(',',str_replace(' ','',@$_SERVER['HTTP_ACCEPT'])) | |
| 			as $mime) | |
| 			if (preg_match('/(.+?)(?:;q=([\d\.]+)|$)/',$mime,$parts)) | |
| 				$accept[$parts[1]]=isset($parts[2])?$parts[2]:1; | |
| 		if (!$accept) | |
| 			$accept['*/*']=1; | |
| 		else { | |
| 			krsort($accept); | |
| 			arsort($accept); | |
| 		} | |
| 		if ($list) { | |
| 			if (is_string($list)) | |
| 				$list=explode(',',$list); | |
| 			foreach ($accept as $mime=>$q) | |
| 				if ($q && $out=preg_grep('/'. | |
| 					str_replace('\*','.*',preg_quote($mime,'/')).'/',$list)) | |
| 					return current($out); | |
| 			return FALSE; | |
| 		} | |
| 		return $accept; | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Transmit file to HTTP client; Return file size if successful, | |
| 	*	FALSE otherwise | |
| 	*	@return int|FALSE | |
| 	*	@param $file string | |
| 	*	@param $mime string | |
| 	*	@param $kbps int | |
| 	*	@param $force bool | |
| 	*	@param $name string | |
| 	*	@param $flush bool | |
| 	**/ | |
| 	function send($file,$mime=NULL,$kbps=0,$force=TRUE,$name=NULL,$flush=TRUE) { | |
| 		if (!is_file($file)) | |
| 			return FALSE; | |
| 		$size=filesize($file); | |
| 		if (PHP_SAPI!='cli') { | |
| 			header('Content-Type: '.($mime?:$this->mime($file))); | |
| 			if ($force) | |
| 				header('Content-Disposition: attachment; '. | |
| 					'filename="'.($name!==NULL?$name:basename($file)).'"'); | |
| 			header('Accept-Ranges: bytes'); | |
| 			header('Content-Length: '.$size); | |
| 			header('X-Powered-By: '.Base::instance()->PACKAGE); | |
| 		} | |
| 		if (!$kbps && $flush) { | |
| 			while (ob_get_level()) | |
| 				ob_end_clean(); | |
| 			readfile($file); | |
| 		} | |
| 		else { | |
| 			$ctr=0; | |
| 			$handle=fopen($file,'rb'); | |
| 			$start=microtime(TRUE); | |
| 			while (!feof($handle) && | |
| 				($info=stream_get_meta_data($handle)) && | |
| 				!$info['timed_out'] && !connection_aborted()) { | |
| 				if ($kbps) { | |
| 					// Throttle output | |
| 					$ctr++; | |
| 					if ($ctr/$kbps>$elapsed=microtime(TRUE)-$start) | |
| 						usleep(1e6*($ctr/$kbps-$elapsed)); | |
| 				} | |
| 				// Send 1KiB and reset timer | |
| 				echo fread($handle,1024); | |
| 				if ($flush) { | |
| 					ob_flush(); | |
| 					flush(); | |
| 				} | |
| 			} | |
| 			fclose($handle); | |
| 		} | |
| 		return $size; | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Receive file(s) from HTTP client | |
| 	*	@return array|bool | |
| 	*	@param $func callback | |
| 	*	@param $overwrite bool | |
| 	*	@param $slug callback|bool | |
| 	**/ | |
| 	function receive($func=NULL,$overwrite=FALSE,$slug=TRUE) { | |
| 		$fw=Base::instance(); | |
| 		$dir=$fw->UPLOADS; | |
| 		if (!is_dir($dir)) | |
| 			mkdir($dir,Base::MODE,TRUE); | |
| 		if ($fw->VERB=='PUT') { | |
| 			$tmp=$fw->TEMP.$fw->SEED.'.'.$fw->hash(uniqid()); | |
| 			if (!$fw->RAW) | |
| 				$fw->write($tmp,$fw->BODY); | |
| 			else { | |
| 				$src=@fopen('php://input','r'); | |
| 				$dst=@fopen($tmp,'w'); | |
| 				if (!$src || !$dst) | |
| 					return FALSE; | |
| 				while (!feof($src) && | |
| 					($info=stream_get_meta_data($src)) && | |
| 					!$info['timed_out'] && $str=fgets($src,4096)) | |
| 					fputs($dst,$str,strlen($str)); | |
| 				fclose($dst); | |
| 				fclose($src); | |
| 			} | |
| 			$base=basename($fw->URI); | |
| 			$file=[ | |
| 				'name'=>$dir. | |
| 					($slug && preg_match('/(.+?)(\.\w+)?$/',$base,$parts)? | |
| 						(is_callable($slug)? | |
| 							$slug($base): | |
| 							($this->slug($parts[1]). | |
| 								(isset($parts[2])?$parts[2]:''))): | |
| 						$base), | |
| 				'tmp_name'=>$tmp, | |
| 				'type'=>$this->mime($base), | |
| 				'size'=>filesize($tmp) | |
| 			]; | |
| 			return (!file_exists($file['name']) || $overwrite) && | |
| 				(!$func || $fw->call($func,[$file])!==FALSE) && | |
| 				rename($tmp,$file['name']); | |
| 		} | |
| 		$fetch=function($arr) use(&$fetch) { | |
| 			if (!is_array($arr)) | |
| 				return [$arr]; | |
| 			$data=[]; | |
| 			foreach($arr as $k=>$sub) | |
| 				$data=array_merge($data,$fetch($sub)); | |
| 			return $data; | |
| 		}; | |
| 		$out=[]; | |
| 		foreach ($_FILES as $name=>$item) { | |
| 			$files=[]; | |
| 			foreach ($item as $k=>$mix) | |
| 				foreach ($fetch($mix) as $i=>$val) | |
| 					$files[$i][$k]=$val; | |
| 			foreach ($files as $file) { | |
| 				if (empty($file['name'])) | |
| 					continue; | |
| 				$base=basename($file['name']); | |
| 				$file['name']=$dir. | |
| 					($slug && preg_match('/(.+?)(\.\w+)?$/',$base,$parts)? | |
| 						(is_callable($slug)? | |
| 							$slug($base,$name): | |
| 							($this->slug($parts[1]). | |
| 								(isset($parts[2])?$parts[2]:''))): | |
| 						$base); | |
| 				$out[$file['name']]=!$file['error'] && | |
| 					(!file_exists($file['name']) || $overwrite) && | |
| 					(!$func || $fw->call($func,[$file,$name])!==FALSE) && | |
| 					move_uploaded_file($file['tmp_name'],$file['name']); | |
| 			} | |
| 		} | |
| 		return $out; | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Return upload progress in bytes, FALSE on failure | |
| 	*	@return int|FALSE | |
| 	*	@param $id string | |
| 	**/ | |
| 	function progress($id) { | |
| 		// ID returned by session.upload_progress.name | |
| 		return ini_get('session.upload_progress.enabled') && | |
| 			isset($_SESSION[$id]['bytes_processed'])? | |
| 				$_SESSION[$id]['bytes_processed']:FALSE; | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	HTTP request via cURL | |
| 	*	@return array | |
| 	*	@param $url string | |
| 	*	@param $options array | |
| 	**/ | |
| 	protected function _curl($url,$options) { | |
| 		$curl=curl_init($url); | |
| 		if (!$open_basedir=ini_get('open_basedir')) | |
| 			curl_setopt($curl,CURLOPT_FOLLOWLOCATION, | |
| 				$options['follow_location']); | |
| 		curl_setopt($curl,CURLOPT_MAXREDIRS, | |
| 			$options['max_redirects']); | |
| 		curl_setopt($curl,CURLOPT_PROTOCOLS,CURLPROTO_HTTP|CURLPROTO_HTTPS); | |
| 		curl_setopt($curl,CURLOPT_REDIR_PROTOCOLS,CURLPROTO_HTTP|CURLPROTO_HTTPS); | |
| 		curl_setopt($curl,CURLOPT_CUSTOMREQUEST,$options['method']); | |
| 		if (isset($options['header'])) | |
| 			curl_setopt($curl,CURLOPT_HTTPHEADER,$options['header']); | |
| 		if (isset($options['content'])) | |
| 			curl_setopt($curl,CURLOPT_POSTFIELDS,$options['content']); | |
| 		if (isset($options['proxy'])) | |
| 			curl_setopt($curl,CURLOPT_PROXY,$options['proxy']); | |
| 		curl_setopt($curl,CURLOPT_ENCODING,'gzip,deflate'); | |
| 		$timeout=isset($options['timeout'])? | |
| 			$options['timeout']: | |
| 			ini_get('default_socket_timeout'); | |
| 		curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,$timeout); | |
| 		curl_setopt($curl,CURLOPT_TIMEOUT,$timeout); | |
| 		$headers=[]; | |
| 		curl_setopt($curl,CURLOPT_HEADERFUNCTION, | |
| 			// Callback for response headers | |
| 			function($curl,$line) use(&$headers) { | |
| 				if ($trim=trim($line)) | |
| 					$headers[]=$trim; | |
| 				return strlen($line); | |
| 			} | |
| 		); | |
| 		curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,2); | |
| 		curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,FALSE); | |
| 		ob_start(); | |
| 		curl_exec($curl); | |
| 		$err=curl_error($curl); | |
| 		curl_close($curl); | |
| 		$body=ob_get_clean(); | |
| 		if (!$err && | |
| 			$options['follow_location'] && $open_basedir && | |
| 			preg_grep('/HTTP\/[\d.]{1,3} 3\d{2}/',$headers) && | |
| 			preg_match('/^Location: (.+)$/m',implode(PHP_EOL,$headers),$loc)) { | |
| 			$options['max_redirects']--; | |
| 			if($loc[1][0] == '/') { | |
| 				$parts=parse_url($url); | |
| 				$loc[1]=$parts['scheme'].'://'.$parts['host']. | |
| 					((isset($parts['port']) && !in_array($parts['port'],[80,443])) | |
| 						?':'.$parts['port']:'').$loc[1]; | |
| 			} | |
| 			return $this->request($loc[1],$options); | |
| 		} | |
| 		return [ | |
| 			'body'=>$body, | |
| 			'headers'=>$headers, | |
| 			'engine'=>'cURL', | |
| 			'cached'=>FALSE, | |
| 			'error'=>$err | |
| 		]; | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	HTTP request via PHP stream wrapper | |
| 	*	@return array | |
| 	*	@param $url string | |
| 	*	@param $options array | |
| 	**/ | |
| 	protected function _stream($url,$options) { | |
| 		$eol="\r\n"; | |
| 		if (isset($options['proxy'])) { | |
| 			$options['proxy']=preg_replace('/https?/i','tcp',$options['proxy']); | |
| 			$options['request_fulluri']=true; | |
| 			if (preg_match('/socks4?/i',$options['proxy'])) | |
| 				return $this->_socket($url,$options); | |
| 		} | |
| 		$options['header']=implode($eol,$options['header']); | |
| 		$body=@file_get_contents($url,FALSE, | |
| 			stream_context_create(['http'=>$options])); | |
| 		$headers=isset($http_response_header)? | |
| 			$http_response_header:[]; | |
| 		$err=''; | |
| 		if (is_string($body)) { | |
| 			$match=NULL; | |
| 			foreach ($headers as $header) | |
| 				if (preg_match('/Content-Encoding: (.+)/i',$header,$match)) | |
| 					break; | |
| 			if ($match) | |
| 				switch ($match[1]) { | |
| 					case 'gzip': | |
| 						$body=gzdecode($body); | |
| 						break; | |
| 					case 'deflate': | |
| 						$body=gzuncompress($body); | |
| 						break; | |
| 				} | |
| 		} | |
| 		else { | |
| 			$tmp=error_get_last(); | |
| 			$err=$tmp['message']; | |
| 		} | |
| 		return [ | |
| 			'body'=>$body, | |
| 			'headers'=>$headers, | |
| 			'engine'=>'stream', | |
| 			'cached'=>FALSE, | |
| 			'error'=>$err | |
| 		]; | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	HTTP request via low-level TCP/IP socket | |
| 	*	@return array | |
| 	*	@param $url string | |
| 	*	@param $options array | |
| 	**/ | |
| 	protected function _socket($url,$options) { | |
| 		$eol="\r\n"; | |
| 		$headers=[]; | |
| 		$body=''; | |
| 		$parts=parse_url($url); | |
| 		$hostname=$parts['host']; | |
| 		$proxy=false; | |
| 		if ($parts['scheme']=='https') | |
| 			$parts['host']='ssl://'.$parts['host']; | |
| 		if (empty($parts['port'])) | |
| 			$parts['port']=$parts['scheme']=='https'?443:80; | |
| 		if (empty($parts['path'])) | |
| 			$parts['path']='/'; | |
| 		if (empty($parts['query'])) | |
| 			$parts['query']=''; | |
| 		if (isset($options['proxy'])) { | |
| 			$req=$url; | |
| 			$pp=parse_url($options['proxy']); | |
| 			$proxy=$pp['scheme']; | |
| 			if ($pp['scheme']=='https') | |
| 				$pp['host']='ssl://'.$pp['host']; | |
| 			if (empty($pp['port'])) | |
| 				$pp['port']=$pp['scheme']=='https'?443:80; | |
| 			$socket=@fsockopen($pp['host'],$pp['port'],$code,$err); | |
| 		} else { | |
| 			$req=$parts['path'].($parts['query']?('?'.$parts['query']):''); | |
| 			$socket=@fsockopen($parts['host'],$parts['port'],$code,$err); | |
| 		} | |
| 		if ($socket) { | |
| 			stream_set_blocking($socket,TRUE); | |
| 			stream_set_timeout($socket,isset($options['timeout'])? | |
| 				$options['timeout']:ini_get('default_socket_timeout')); | |
| 			if ($proxy=='socks4') { | |
| 				// SOCKS4; http://en.wikipedia.org/wiki/SOCKS#Protocol | |
| 				$packet="\x04\x01".pack("n", $parts['port']). | |
| 					pack("H*",dechex(ip2long(gethostbyname($hostname))))."\0"; | |
| 				fputs($socket, $packet, strlen($packet)); | |
| 				$response=fread($socket, 9); | |
| 				if (strlen($response)==8 && (ord($response[0])==0 || ord($response[0])==4) | |
| 					&& ord($response[1])==90) { | |
| 					$options['header'][]='Host: '.$hostname; | |
| 				} else | |
| 					$err='Socket Status '.ord($response[1]); | |
| 			} | |
| 			fputs($socket,$options['method'].' '.$req.' HTTP/1.0'.$eol); | |
| 			fputs($socket,implode($eol,$options['header']).$eol.$eol); | |
| 			if (isset($options['content'])) | |
| 				fputs($socket,$options['content'].$eol); | |
| 			// Get response | |
| 			$content=''; | |
| 			while (!feof($socket) && | |
| 				($info=stream_get_meta_data($socket)) && | |
| 				!$info['timed_out'] && !connection_aborted() && | |
| 				$str=fgets($socket,4096)) | |
| 				$content.=$str; | |
| 			fclose($socket); | |
| 			$html=explode($eol.$eol,$content,2); | |
| 			$body=isset($html[1])?$html[1]:''; | |
| 			$headers=array_merge($headers,$current=explode($eol,$html[0])); | |
| 			$match=NULL; | |
| 			foreach ($current as $header) | |
| 				if (preg_match('/Content-Encoding: (.+)/i',$header,$match)) | |
| 					break; | |
| 			if ($match) | |
| 				switch ($match[1]) { | |
| 					case 'gzip': | |
| 						$body=gzdecode($body); | |
| 						break; | |
| 					case 'deflate': | |
| 						$body=gzuncompress($body); | |
| 						break; | |
| 				} | |
| 			if ($options['follow_location'] && | |
| 				preg_grep('/HTTP\/[\d.]{1,3} 3\d{2}/',$headers) && | |
| 				preg_match('/Location: (.+?)'.preg_quote($eol).'/', | |
| 				$html[0],$loc)) { | |
| 				$options['max_redirects']--; | |
| 				return $this->request($loc[1],$options); | |
| 			} | |
| 		} | |
| 		return [ | |
| 			'body'=>$body, | |
| 			'headers'=>$headers, | |
| 			'engine'=>'socket', | |
| 			'cached'=>FALSE, | |
| 			'error'=>$err | |
| 		]; | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Specify the HTTP request engine to use; If not available, | |
| 	*	fall back to an applicable substitute | |
| 	*	@return string | |
| 	*	@param $arg string | |
| 	**/ | |
| 	function engine($arg='curl') { | |
| 		$arg=strtolower($arg); | |
| 		$flags=[ | |
| 			'curl'=>extension_loaded('curl'), | |
| 			'stream'=>ini_get('allow_url_fopen'), | |
| 			'socket'=>function_exists('fsockopen') | |
| 		]; | |
| 		if ($flags[$arg]) | |
| 			return $this->wrapper=$arg; | |
| 		foreach ($flags as $key=>$val) | |
| 			if ($val) | |
| 				return $this->wrapper=$key; | |
| 		user_error(self::E_Request,E_USER_ERROR); | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Replace old headers with new elements | |
| 	*	@return NULL | |
| 	*	@param $old array | |
| 	*	@param $new string|array | |
| 	**/ | |
| 	function subst(array &$old,$new) { | |
| 		if (is_string($new)) | |
| 			$new=[$new]; | |
| 		foreach ($new as $hdr) { | |
| 			$old=preg_grep('/'.preg_quote(strstr($hdr,':',TRUE),'/').':.+/', | |
| 				$old,PREG_GREP_INVERT); | |
| 			array_push($old,$hdr); | |
| 		} | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Submit HTTP request; Use HTTP context options (described in | |
| 	*	http://www.php.net/manual/en/context.http.php) if specified; | |
| 	*	Cache the page as instructed by remote server | |
| 	*	@return array|FALSE | |
| 	*	@param $url string | |
| 	*	@param $options array | |
| 	**/ | |
| 	function request($url,array $options=NULL) { | |
| 		$fw=Base::instance(); | |
| 		$parts=parse_url($url); | |
| 		if (empty($parts['scheme'])) { | |
| 			// Local URL | |
| 			$url=$fw->SCHEME.'://'.$fw->HOST. | |
| 				(in_array($fw->PORT,[80,443])?'':(':'.$fw->PORT)). | |
| 				($url[0]!='/'?($fw->BASE.'/'):'').$url; | |
| 			$parts=parse_url($url); | |
| 		} | |
| 		elseif (!preg_match('/https?/',$parts['scheme'])) | |
| 			return FALSE; | |
| 		if (!is_array($options)) | |
| 			$options=[]; | |
| 		if (empty($options['header'])) | |
| 			$options['header']=[]; | |
| 		elseif (is_string($options['header'])) | |
| 			$options['header']=[$options['header']]; | |
| 		if (!$this->wrapper) | |
| 			$this->engine(); | |
| 		if ($this->wrapper!='stream') { | |
| 			// PHP streams can't cope with redirects when Host header is set | |
| 			$this->subst($options['header'],'Host: '.$parts['host']); | |
| 		} | |
| 		$this->subst($options['header'], | |
| 			[ | |
| 				'Accept-Encoding: gzip,deflate', | |
| 				'User-Agent: '.(isset($options['user_agent'])? | |
| 					$options['user_agent']: | |
| 					'Mozilla/5.0 (compatible; '.php_uname('s').')'), | |
| 				'Connection: close' | |
| 			] | |
| 		); | |
| 		if (isset($options['content']) && is_string($options['content'])) { | |
| 			if ($options['method']=='POST' && | |
| 				!preg_grep('/^Content-Type:/i',$options['header'])) | |
| 				$this->subst($options['header'], | |
| 					'Content-Type: application/x-www-form-urlencoded'); | |
| 			$this->subst($options['header'], | |
| 				'Content-Length: '.strlen($options['content'])); | |
| 		} | |
| 		if (isset($parts['user'],$parts['pass'])) | |
| 			$this->subst($options['header'], | |
| 				'Authorization: Basic '. | |
| 					base64_encode($parts['user'].':'.$parts['pass']) | |
| 			); | |
| 		$options+=[ | |
| 			'method'=>'GET', | |
| 			'header'=>$options['header'], | |
| 			'follow_location'=>TRUE, | |
| 			'max_redirects'=>20, | |
| 			'ignore_errors'=>FALSE | |
| 		]; | |
| 		$eol="\r\n"; | |
| 		if ($fw->CACHE && | |
| 			preg_match('/GET|HEAD/',$options['method'])) { | |
| 			$cache=Cache::instance(); | |
| 			if ($cache->exists( | |
| 				$hash=$fw->hash($options['method'].' '.$url).'.url',$data)) { | |
| 				if (preg_match('/Last-Modified: (.+?)'.preg_quote($eol).'/', | |
| 					implode($eol,$data['headers']),$mod)) | |
| 					$this->subst($options['header'], | |
| 						'If-Modified-Since: '.$mod[1]); | |
| 			} | |
| 		} | |
| 		$result=$this->{'_'.$this->wrapper}($url,$options); | |
| 		if ($result && isset($cache)) { | |
| 			if (preg_match('/HTTP\/[\d.]{1,3} 304/', | |
| 				implode($eol,$result['headers']))) { | |
| 				$result=$cache->get($hash); | |
| 				$result['cached']=TRUE; | |
| 			} | |
| 			elseif (preg_match('/Cache-Control:(?:.*)max-age=(\d+)(?:,?.*'. | |
| 				preg_quote($eol).')/i',implode($eol,$result['headers']),$exp)) | |
| 				$cache->set($hash,$result,$exp[1]); | |
| 		} | |
| 		$req=[$options['method'].' '.$url]; | |
| 		foreach ($options['header'] as $header) | |
| 			array_push($req,$header); | |
| 		return array_merge(['request'=>$req],$result); | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Strip Javascript/CSS files of extraneous whitespaces and comments; | |
| 	*	Return combined output as a minified string | |
| 	*	@return string | |
| 	*	@param $files string|array | |
| 	*	@param $mime string | |
| 	*	@param $header bool | |
| 	*	@param $path string | |
| 	**/ | |
| 	function minify($files,$mime=NULL,$header=TRUE,$path=NULL) { | |
| 		$fw=Base::instance(); | |
| 		if (is_string($files)) | |
| 			$files=$fw->split($files); | |
| 		if (!$mime) | |
| 			$mime=$this->mime($files[0]); | |
| 		preg_match('/\w+$/',$files[0],$ext); | |
| 		$cache=Cache::instance(); | |
| 		$dst=''; | |
| 		if (!isset($path)) | |
| 			$path=$fw->UI.';./'; | |
| 		foreach (array_unique($fw->split($path,FALSE)) as $dir) | |
| 			foreach ($files as $i=>$file) | |
| 				if (is_file($save=$fw->fixslashes($dir.$file)) && | |
| 					is_bool(strpos($save,'../')) && | |
| 					preg_match('/\.(css|js)$/i',$file)) { | |
| 					unset($files[$i]); | |
| 					if ($fw->CACHE && | |
| 						($cached=$cache->exists( | |
| 							$hash=$fw->hash($save).'.'.$ext[0],$data)) && | |
| 						$cached[0]>filemtime($save)) | |
| 						$dst.=$data; | |
| 					else { | |
| 						$data=''; | |
| 						$src=$fw->read($save); | |
| 						for ($ptr=0,$len=strlen($src);$ptr<$len;) { | |
| 							if (preg_match('/^@import\h+url'. | |
| 								'\(\h*([\'"])((?!(?:https?:)?\/\/).+?)\1\h*\)[^;]*;/', | |
| 								substr($src,$ptr),$parts)) { | |
| 								$path=dirname($file); | |
| 								$data.=$this->minify( | |
| 									($path?($path.'/'):'').$parts[2], | |
| 									$mime,$header | |
| 								); | |
| 								$ptr+=strlen($parts[0]); | |
| 								continue; | |
| 							} | |
| 							if ($ext[0]=='css'&&preg_match('/^url\(([^\'"].*?[^\'"])\)/i', | |
| 									substr($src,$ptr),$parts)) { | |
| 								$data.=$parts[0]; | |
| 								$ptr+=strlen($parts[0]); | |
| 								continue; | |
| 							} | |
| 							if ($src[$ptr]=='/') { | |
| 								if ($src[$ptr+1]=='*') { | |
| 									// Multiline comment | |
| 									$str=strstr( | |
| 										substr($src,$ptr+2),'*/',TRUE); | |
| 									$ptr+=strlen($str)+4; | |
| 								} | |
| 								elseif ($src[$ptr+1]=='/') { | |
| 									// Single-line comment | |
| 									$str=strstr( | |
| 										substr($src,$ptr+2),"\n",TRUE); | |
| 									$ptr+=(empty($str))? | |
| 										strlen(substr($src,$ptr)):strlen($str)+2; | |
| 								} | |
| 								else { | |
| 									// Presume it's a regex pattern | |
| 									$regex=TRUE; | |
| 									// Backtrack and validate | |
| 									for ($ofs=$ptr;$ofs;$ofs--) { | |
| 										// Pattern should be preceded by | |
| 										// open parenthesis, colon, | |
| 										// object property or operator | |
| 										if (preg_match( | |
| 											'/(return|[(:=!+\-*&|])$/', | |
| 											substr($src,0,$ofs))) { | |
| 											$data.='/'; | |
| 											$ptr++; | |
| 											while ($ptr<$len) { | |
| 												$data.=$src[$ptr]; | |
| 												$ptr++; | |
| 												if ($src[$ptr-1]=='\\') { | |
| 													$data.=$src[$ptr]; | |
| 													$ptr++; | |
| 												} | |
| 												elseif ($src[$ptr-1]=='/') | |
| 													break; | |
| 											} | |
| 											break; | |
| 										} | |
| 										elseif (!ctype_space($src[$ofs-1])) { | |
| 											// Not a regex pattern | |
| 											$regex=FALSE; | |
| 											break; | |
| 										} | |
| 									} | |
| 									if (!$regex) { | |
| 										// Division operator | |
| 										$data.=$src[$ptr]; | |
| 										$ptr++; | |
| 									} | |
| 								} | |
| 								continue; | |
| 							} | |
| 							if (in_array($src[$ptr],['\'','"','`'])) { | |
| 								$match=$src[$ptr]; | |
| 								$data.=$match; | |
| 								$ptr++; | |
| 								// String literal | |
| 								while ($ptr<$len) { | |
| 									$data.=$src[$ptr]; | |
| 									$ptr++; | |
| 									if ($src[$ptr-1]=='\\') { | |
| 										$data.=$src[$ptr]; | |
| 										$ptr++; | |
| 									} | |
| 									elseif ($src[$ptr-1]==$match) | |
| 										break; | |
| 								} | |
| 								continue; | |
| 							} | |
| 							if (ctype_space($src[$ptr])) { | |
| 								if ($ptr+1<strlen($src) && | |
| 									preg_match('/[\w'.($ext[0]=='css'? | |
| 										'#\.%+\-*()\[\]':'\$').']{2}|'. | |
| 										'[+\-]{2}/', | |
| 										substr($data,-1).$src[$ptr+1]) || | |
| 									($ext[0]=='css' && $ptr+2<strlen($src) && | |
| 									preg_match('/:\w/',substr($src,$ptr+1,2)))) | |
| 									$data.=' '; | |
| 								$ptr++; | |
| 								continue; | |
| 							} | |
| 							$data.=$src[$ptr]; | |
| 							$ptr++; | |
| 						} | |
| 						if ($ext[0]=='css') | |
| 							$data=str_replace(';}','}',$data); | |
| 						if ($fw->CACHE) | |
| 							$cache->set($hash,$data); | |
| 						$dst.=$data; | |
| 					} | |
| 				} | |
| 		if (PHP_SAPI!='cli' && $header) | |
| 			header('Content-Type: '.$mime.'; charset='.$fw->ENCODING); | |
| 		return $dst; | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Retrieve RSS feed and return as an array | |
| 	*	@return array|FALSE | |
| 	*	@param $url string | |
| 	*	@param $max int | |
| 	*	@param $tags string | |
| 	**/ | |
| 	function rss($url,$max=10,$tags=NULL) { | |
| 		if (!$data=$this->request($url)) | |
| 			return FALSE; | |
| 		// Suppress errors caused by invalid XML structures | |
| 		libxml_use_internal_errors(TRUE); | |
| 		$xml=simplexml_load_string($data['body'], | |
| 			NULL,LIBXML_NOBLANKS|LIBXML_NOERROR); | |
| 		if (!is_object($xml)) | |
| 			return FALSE; | |
| 		$out=[]; | |
| 		if (isset($xml->channel)) { | |
| 			$out['source']=(string)$xml->channel->title; | |
| 			$max=min($max,count($xml->channel->item)); | |
| 			for ($i=0;$i<$max;$i++) { | |
| 				$item=$xml->channel->item[$i]; | |
| 				$list=[''=>NULL]+$item->getnamespaces(TRUE); | |
| 				$fields=[]; | |
| 				foreach ($list as $ns=>$uri) | |
| 					foreach ($item->children($uri) as $key=>$val) | |
| 						$fields[$ns.($ns?':':'').$key]=(string)$val; | |
| 				$out['feed'][]=$fields; | |
| 			} | |
| 		} | |
| 		else | |
| 			return FALSE; | |
| 		Base::instance()->scrub($out,$tags); | |
| 		return $out; | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Retrieve information from whois server | |
| 	*	@return string|FALSE | |
| 	*	@param $addr string | |
| 	*	@param $server string | |
| 	**/ | |
| 	function whois($addr,$server='whois.internic.net') { | |
| 		$socket=@fsockopen($server,43,$errno,$errstr); | |
| 		if (!$socket) | |
| 			// Can't establish connection | |
| 			return FALSE; | |
| 		// Set connection timeout parameters | |
| 		stream_set_blocking($socket,FALSE); | |
| 		stream_set_timeout($socket,ini_get('default_socket_timeout')); | |
| 		// Send request | |
| 		fputs($socket,$addr."\r\n"); | |
| 		$info=stream_get_meta_data($socket); | |
| 		// Get response | |
| 		$response=''; | |
| 		while (!feof($socket) && !$info['timed_out']) { | |
| 			$response.=fgets($socket,4096); // MDFK97 | |
| 			$info=stream_get_meta_data($socket); | |
| 		} | |
| 		fclose($socket); | |
| 		return $info['timed_out']?FALSE:trim($response); | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Return preset diacritics translation table | |
| 	*	@return array | |
| 	**/ | |
| 	function diacritics() { | |
| 		return [ | |
| 			'Ǎ'=>'A','А'=>'A','Ā'=>'A','Ă'=>'A','Ą'=>'A','Å'=>'A', | |
| 			'Ǻ'=>'A','Ä'=>'Ae','Á'=>'A','À'=>'A','Ã'=>'A','Â'=>'A', | |
| 			'Æ'=>'AE','Ǽ'=>'AE','Б'=>'B','Ç'=>'C','Ć'=>'C','Ĉ'=>'C', | |
| 			'Č'=>'C','Ċ'=>'C','Ц'=>'C','Ч'=>'Ch','Ð'=>'Dj','Đ'=>'Dj', | |
| 			'Ď'=>'Dj','Д'=>'Dj','É'=>'E','Ę'=>'E','Ё'=>'E','Ė'=>'E', | |
| 			'Ê'=>'E','Ě'=>'E','Ē'=>'E','È'=>'E','Е'=>'E','Э'=>'E', | |
| 			'Ë'=>'E','Ĕ'=>'E','Ф'=>'F','Г'=>'G','Ģ'=>'G','Ġ'=>'G', | |
| 			'Ĝ'=>'G','Ğ'=>'G','Х'=>'H','Ĥ'=>'H','Ħ'=>'H','Ï'=>'I', | |
| 			'Ĭ'=>'I','İ'=>'I','Į'=>'I','Ī'=>'I','Í'=>'I','Ì'=>'I', | |
| 			'И'=>'I','Ǐ'=>'I','Ĩ'=>'I','Î'=>'I','IJ'=>'IJ','Ĵ'=>'J', | |
| 			'Й'=>'J','Я'=>'Ja','Ю'=>'Ju','К'=>'K','Ķ'=>'K','Ĺ'=>'L', | |
| 			'Л'=>'L','Ł'=>'L','Ŀ'=>'L','Ļ'=>'L','Ľ'=>'L','М'=>'M', | |
| 			'Н'=>'N','Ń'=>'N','Ñ'=>'N','Ņ'=>'N','Ň'=>'N','Ō'=>'O', | |
| 			'О'=>'O','Ǿ'=>'O','Ǒ'=>'O','Ơ'=>'O','Ŏ'=>'O','Ő'=>'O', | |
| 			'Ø'=>'O','Ö'=>'Oe','Õ'=>'O','Ó'=>'O','Ò'=>'O','Ô'=>'O', | |
| 			'Œ'=>'OE','П'=>'P','Ŗ'=>'R','Р'=>'R','Ř'=>'R','Ŕ'=>'R', | |
| 			'Ŝ'=>'S','Ş'=>'S','Š'=>'S','Ș'=>'S','Ś'=>'S','С'=>'S', | |
| 			'Ш'=>'Sh','Щ'=>'Shch','Ť'=>'T','Ŧ'=>'T','Ţ'=>'T','Ț'=>'T', | |
| 			'Т'=>'T','Ů'=>'U','Ű'=>'U','Ŭ'=>'U','Ũ'=>'U','Ų'=>'U', | |
| 			'Ū'=>'U','Ǜ'=>'U','Ǚ'=>'U','Ù'=>'U','Ú'=>'U','Ü'=>'Ue', | |
| 			'Ǘ'=>'U','Ǖ'=>'U','У'=>'U','Ư'=>'U','Ǔ'=>'U','Û'=>'U', | |
| 			'В'=>'V','Ŵ'=>'W','Ы'=>'Y','Ŷ'=>'Y','Ý'=>'Y','Ÿ'=>'Y', | |
| 			'Ź'=>'Z','З'=>'Z','Ż'=>'Z','Ž'=>'Z','Ж'=>'Zh','á'=>'a', | |
| 			'ă'=>'a','â'=>'a','à'=>'a','ā'=>'a','ǻ'=>'a','å'=>'a', | |
| 			'ä'=>'ae','ą'=>'a','ǎ'=>'a','ã'=>'a','а'=>'a','ª'=>'a', | |
| 			'æ'=>'ae','ǽ'=>'ae','б'=>'b','č'=>'c','ç'=>'c','ц'=>'c', | |
| 			'ċ'=>'c','ĉ'=>'c','ć'=>'c','ч'=>'ch','ð'=>'dj','ď'=>'dj', | |
| 			'д'=>'dj','đ'=>'dj','э'=>'e','é'=>'e','ё'=>'e','ë'=>'e', | |
| 			'ê'=>'e','е'=>'e','ĕ'=>'e','è'=>'e','ę'=>'e','ě'=>'e', | |
| 			'ė'=>'e','ē'=>'e','ƒ'=>'f','ф'=>'f','ġ'=>'g','ĝ'=>'g', | |
| 			'ğ'=>'g','г'=>'g','ģ'=>'g','х'=>'h','ĥ'=>'h','ħ'=>'h', | |
| 			'ǐ'=>'i','ĭ'=>'i','и'=>'i','ī'=>'i','ĩ'=>'i','į'=>'i', | |
| 			'ı'=>'i','ì'=>'i','î'=>'i','í'=>'i','ï'=>'i','ij'=>'ij', | |
| 			'ĵ'=>'j','й'=>'j','я'=>'ja','ю'=>'ju','ķ'=>'k','к'=>'k', | |
| 			'ľ'=>'l','ł'=>'l','ŀ'=>'l','ĺ'=>'l','ļ'=>'l','л'=>'l', | |
| 			'м'=>'m','ņ'=>'n','ñ'=>'n','ń'=>'n','н'=>'n','ň'=>'n', | |
| 			'ʼn'=>'n','ó'=>'o','ò'=>'o','ǒ'=>'o','ő'=>'o','о'=>'o', | |
| 			'ō'=>'o','º'=>'o','ơ'=>'o','ŏ'=>'o','ô'=>'o','ö'=>'oe', | |
| 			'õ'=>'o','ø'=>'o','ǿ'=>'o','œ'=>'oe','п'=>'p','р'=>'r', | |
| 			'ř'=>'r','ŕ'=>'r','ŗ'=>'r','ſ'=>'s','ŝ'=>'s','ș'=>'s', | |
| 			'š'=>'s','ś'=>'s','с'=>'s','ş'=>'s','ш'=>'sh','щ'=>'shch', | |
| 			'ß'=>'ss','ţ'=>'t','т'=>'t','ŧ'=>'t','ť'=>'t','ț'=>'t', | |
| 			'у'=>'u','ǘ'=>'u','ŭ'=>'u','û'=>'u','ú'=>'u','ų'=>'u', | |
| 			'ù'=>'u','ű'=>'u','ů'=>'u','ư'=>'u','ū'=>'u','ǚ'=>'u', | |
| 			'ǜ'=>'u','ǔ'=>'u','ǖ'=>'u','ũ'=>'u','ü'=>'ue','в'=>'v', | |
| 			'ŵ'=>'w','ы'=>'y','ÿ'=>'y','ý'=>'y','ŷ'=>'y','ź'=>'z', | |
| 			'ž'=>'z','з'=>'z','ż'=>'z','ж'=>'zh','ь'=>'','ъ'=>'', | |
| 			'њ'=>'nj','љ'=>'lj','ђ'=>'dj','џ'=>'dz','ћ'=>'c','ј'=>'j', | |
| 			'\''=>'', | |
| 		]; | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Return a URL/filesystem-friendly version of string | |
| 	*	@return string | |
| 	*	@param $text string | |
| 	**/ | |
| 	function slug($text) { | |
| 		return trim(strtolower(preg_replace('/([^\pL\pN])+/u','-', | |
| 			trim(strtr($text,Base::instance()->DIACRITICS+$this->diacritics())))),'-'); | |
| 	} | |
| 
 | |
| 	/** | |
| 	*	Return chunk of text from standard Lorem Ipsum passage | |
| 	*	@return string | |
| 	*	@param $count int | |
| 	*	@param $max int | |
| 	*	@param $std bool | |
| 	**/ | |
| 	function filler($count=1,$max=20,$std=TRUE) { | |
| 		$out=''; | |
| 		if ($std) | |
| 			$out='Lorem ipsum dolor sit amet, consectetur adipisicing elit, '. | |
| 				'sed do eiusmod tempor incididunt ut labore et dolore magna '. | |
| 				'aliqua.'; | |
| 		$rnd=explode(' ', | |
| 			'a ab ad accusamus adipisci alias aliquam amet animi aperiam '. | |
| 			'architecto asperiores aspernatur assumenda at atque aut beatae '. | |
| 			'blanditiis cillum commodi consequatur corporis corrupti culpa '. | |
| 			'cum cupiditate debitis delectus deleniti deserunt dicta '. | |
| 			'dignissimos distinctio dolor ducimus duis ea eaque earum eius '. | |
| 			'eligendi enim eos error esse est eum eveniet ex excepteur '. | |
| 			'exercitationem expedita explicabo facere facilis fugiat harum '. | |
| 			'hic id illum impedit in incidunt ipsa iste itaque iure iusto '. | |
| 			'laborum laudantium libero magnam maiores maxime minim minus '. | |
| 			'modi molestiae mollitia nam natus necessitatibus nemo neque '. | |
| 			'nesciunt nihil nisi nobis non nostrum nulla numquam occaecati '. | |
| 			'odio officia omnis optio pariatur perferendis perspiciatis '. | |
| 			'placeat porro possimus praesentium proident quae quia quibus '. | |
| 			'quo ratione recusandae reiciendis rem repellat reprehenderit '. | |
| 			'repudiandae rerum saepe sapiente sequi similique sint soluta '. | |
| 			'suscipit tempora tenetur totam ut ullam unde vel veniam vero '. | |
| 			'vitae voluptas'); | |
| 		for ($i=0,$add=$count-(int)$std;$i<$add;$i++) { | |
| 			shuffle($rnd); | |
| 			$words=array_slice($rnd,0,mt_rand(3,$max)); | |
| 			$out.=(!$std&&$i==0?'':' ').ucfirst(implode(' ',$words)).'.'; | |
| 		} | |
| 		return $out; | |
| 	} | |
| 
 | |
| } | |
| 
 | |
| if (!function_exists('gzdecode')) { | |
| 
 | |
| 	/** | |
| 	*	Decode gzip-compressed string | |
| 	*	@param $str string | |
| 	*	@return string | |
| 	**/ | |
| 	function gzdecode($str) { | |
| 		$fw=Base::instance(); | |
| 		if (!is_dir($tmp=$fw->TEMP)) | |
| 			mkdir($tmp,Base::MODE,TRUE); | |
| 		file_put_contents($file=$tmp.'/'.$fw->SEED.'.'. | |
| 			$fw->hash(uniqid(NULL,TRUE)).'.gz',$str,LOCK_EX); | |
| 		ob_start(); | |
| 		readgzfile($file); | |
| 		$out=ob_get_clean(); | |
| 		@unlink($file); | |
| 		return $out; | |
| 	} | |
| 
 | |
| }
 | |
| 
 |