web.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838
  1. <?php
  2. /*
  3. Copyright (c) 2009-2014 F3::Factory/Bong Cosca, All rights reserved.
  4. This file is part of the Fat-Free Framework (http://fatfree.sf.net).
  5. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
  6. ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  7. IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  8. PURPOSE.
  9. Please see the license.txt file for more information.
  10. */
  11. //! Wrapper for various HTTP utilities
  12. class Web extends Prefab {
  13. //@{ Error messages
  14. const
  15. E_Request='No suitable HTTP request engine found';
  16. //@}
  17. protected
  18. //! HTTP request engine
  19. $wrapper;
  20. /**
  21. * Detect MIME type using file extension
  22. * @return string
  23. * @param $file string
  24. **/
  25. function mime($file) {
  26. if (preg_match('/\w+$/',$file,$ext)) {
  27. $map=array(
  28. 'au'=>'audio/basic',
  29. 'avi'=>'video/avi',
  30. 'bmp'=>'image/bmp',
  31. 'bz2'=>'application/x-bzip2',
  32. 'css'=>'text/css',
  33. 'dtd'=>'application/xml-dtd',
  34. 'doc'=>'application/msword',
  35. 'gif'=>'image/gif',
  36. 'gz'=>'application/x-gzip',
  37. 'hqx'=>'application/mac-binhex40',
  38. 'html?'=>'text/html',
  39. 'jar'=>'application/java-archive',
  40. 'jpe?g'=>'image/jpeg',
  41. 'js'=>'application/x-javascript',
  42. 'midi'=>'audio/x-midi',
  43. 'mp3'=>'audio/mpeg',
  44. 'mpe?g'=>'video/mpeg',
  45. 'ogg'=>'audio/vorbis',
  46. 'pdf'=>'application/pdf',
  47. 'png'=>'image/png',
  48. 'ppt'=>'application/vnd.ms-powerpoint',
  49. 'ps'=>'application/postscript',
  50. 'qt'=>'video/quicktime',
  51. 'ram?'=>'audio/x-pn-realaudio',
  52. 'rdf'=>'application/rdf',
  53. 'rtf'=>'application/rtf',
  54. 'sgml?'=>'text/sgml',
  55. 'sit'=>'application/x-stuffit',
  56. 'svg'=>'image/svg+xml',
  57. 'swf'=>'application/x-shockwave-flash',
  58. 'tgz'=>'application/x-tar',
  59. 'tiff'=>'image/tiff',
  60. 'txt'=>'text/plain',
  61. 'wav'=>'audio/wav',
  62. 'xls'=>'application/vnd.ms-excel',
  63. 'xml'=>'application/xml',
  64. 'zip'=>'application/x-zip-compressed'
  65. );
  66. foreach ($map as $key=>$val)
  67. if (preg_match('/'.$key.'/',strtolower($ext[0])))
  68. return $val;
  69. }
  70. return 'application/octet-stream';
  71. }
  72. /**
  73. * Return the MIME types stated in the HTTP Accept header as an array;
  74. * If a list of MIME types is specified, return the best match; or
  75. * FALSE if none found
  76. * @return array|string|FALSE
  77. * @param $list string|array
  78. **/
  79. function acceptable($list=NULL) {
  80. $accept=array();
  81. foreach (explode(',',str_replace(' ','',$_SERVER['HTTP_ACCEPT']))
  82. as $mime)
  83. if (preg_match('/(.+?)(?:;q=([\d\.]+)|$)/',$mime,$parts))
  84. $accept[$parts[1]]=isset($parts[2])?$parts[2]:1;
  85. if (!$accept)
  86. $accept['*/*']=1;
  87. else {
  88. krsort($accept);
  89. arsort($accept);
  90. }
  91. if ($list) {
  92. if (is_string($list))
  93. $list=explode(',',$list);
  94. foreach ($accept as $mime=>$q)
  95. if ($q && $out=preg_grep('/'.
  96. str_replace('\*','.*',preg_quote($mime,'/')).'/',$list))
  97. return current($out);
  98. return FALSE;
  99. }
  100. return $accept;
  101. }
  102. /**
  103. * Transmit file to HTTP client; Return file size if successful,
  104. * FALSE otherwise
  105. * @return int|FALSE
  106. * @param $file string
  107. * @param $mime string
  108. * @param $kbps int
  109. * @param $force bool
  110. **/
  111. function send($file,$mime=NULL,$kbps=0,$force=TRUE) {
  112. if (!is_file($file))
  113. return FALSE;
  114. if (PHP_SAPI!='cli') {
  115. header('Content-Type: '.($mime?:$this->mime($file)));
  116. if ($force)
  117. header('Content-Disposition: attachment; '.
  118. 'filename='.basename($file));
  119. header('Accept-Ranges: bytes');
  120. header('Content-Length: '.$size=filesize($file));
  121. header('X-Powered-By: '.Base::instance()->get('PACKAGE'));
  122. }
  123. $ctr=0;
  124. $handle=fopen($file,'rb');
  125. $start=microtime(TRUE);
  126. while (!feof($handle) &&
  127. ($info=stream_get_meta_data($handle)) &&
  128. !$info['timed_out'] && !connection_aborted()) {
  129. if ($kbps) {
  130. // Throttle output
  131. $ctr++;
  132. if ($ctr/$kbps>$elapsed=microtime(TRUE)-$start)
  133. usleep(1e6*($ctr/$kbps-$elapsed));
  134. }
  135. // Send 1KiB and reset timer
  136. echo fread($handle,1024);
  137. }
  138. fclose($handle);
  139. return $size;
  140. }
  141. /**
  142. * Receive file(s) from HTTP client
  143. * @return array|bool
  144. * @param $func callback
  145. * @param $overwrite bool
  146. * @param $slug callback|bool
  147. **/
  148. function receive($func=NULL,$overwrite=FALSE,$slug=TRUE) {
  149. $fw=Base::instance();
  150. $dir=$fw->get('UPLOADS');
  151. if (!is_dir($dir))
  152. mkdir($dir,Base::MODE,TRUE);
  153. if ($fw->get('VERB')=='PUT') {
  154. $tmp=$fw->get('TEMP').
  155. $fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
  156. $fw->hash(uniqid());
  157. if (!$fw->get('RAW'))
  158. $fw->write($tmp,$fw->get('BODY'));
  159. else {
  160. $src=@fopen('php://input','r');
  161. $dst=@fopen($tmp,'w');
  162. if (!$src || !$dst)
  163. return FALSE;
  164. while (!feof($src) &&
  165. ($info=stream_get_meta_data($src)) &&
  166. !$info['timed_out'] && $str=fgets($src,4096))
  167. fputs($dst,$str,strlen($str));
  168. fclose($dst);
  169. fclose($src);
  170. }
  171. $base=basename($fw->get('URI'));
  172. $file=array(
  173. 'name'=>$dir.
  174. ($slug && preg_match('/(.+?)(\.\w+)?$/',$base,$parts)?
  175. (is_callable($slug)?
  176. $slug($base):
  177. ($this->slug($parts[1]).
  178. (isset($parts[2])?$parts[2]:''))):
  179. $base),
  180. 'tmp_name'=>$tmp,
  181. 'type'=>$this->mime($base),
  182. 'size'=>filesize($tmp)
  183. );
  184. return (!file_exists($file['name']) || $overwrite) &&
  185. (!$func || $fw->call($func,array($file))!==FALSE) &&
  186. rename($tmp,$file['name']);
  187. }
  188. $out=array();
  189. foreach ($_FILES as $name=>$item) {
  190. if (is_array($item['name'])) {
  191. // Transpose array
  192. $tmp=array();
  193. foreach ($item as $keyx=>$cols)
  194. foreach ($cols as $keyy=>$valy)
  195. $tmp[$keyy][$keyx]=$valy;
  196. $item=$tmp;
  197. }
  198. else
  199. $item=array($item);
  200. foreach ($item as $file) {
  201. if (empty($file['name']))
  202. continue;
  203. $base=basename($file['name']);
  204. $file['name']=$dir.
  205. ($slug && preg_match('/(.+?)(\.\w+)?$/',$base,$parts)?
  206. (is_callable($slug)?
  207. $slug($base):
  208. ($this->slug($parts[1]).
  209. (isset($parts[2])?$parts[2]:''))):
  210. $base);
  211. $out[$file['name']]=!$file['error'] &&
  212. is_uploaded_file($file['tmp_name']) &&
  213. (!file_exists($file['name']) || $overwrite) &&
  214. (!$func || $fw->call($func,array($file,$name))!==FALSE) &&
  215. move_uploaded_file($file['tmp_name'],$file['name']);
  216. }
  217. }
  218. return $out;
  219. }
  220. /**
  221. * Return upload progress in bytes, FALSE on failure
  222. * @return int|FALSE
  223. * @param $id string
  224. **/
  225. function progress($id) {
  226. // ID returned by session.upload_progress.name
  227. return ini_get('session.upload_progress.enabled') &&
  228. isset($_SESSION[$id]['bytes_processed'])?
  229. $_SESSION[$id]['bytes_processed']:FALSE;
  230. }
  231. /**
  232. * HTTP request via cURL
  233. * @return array
  234. * @param $url string
  235. * @param $options array
  236. **/
  237. protected function _curl($url,$options) {
  238. $curl=curl_init($url);
  239. curl_setopt($curl,CURLOPT_FOLLOWLOCATION,
  240. $options['follow_location']);
  241. curl_setopt($curl,CURLOPT_MAXREDIRS,
  242. $options['max_redirects']);
  243. curl_setopt($curl,CURLOPT_CUSTOMREQUEST,$options['method']);
  244. if (isset($options['header']))
  245. curl_setopt($curl,CURLOPT_HTTPHEADER,$options['header']);
  246. if (isset($options['user_agent']))
  247. curl_setopt($curl,CURLOPT_USERAGENT,$options['user_agent']);
  248. if (isset($options['content']))
  249. curl_setopt($curl,CURLOPT_POSTFIELDS,$options['content']);
  250. curl_setopt($curl,CURLOPT_ENCODING,'gzip,deflate');
  251. $timeout=isset($options['timeout'])?
  252. $options['timeout']:
  253. ini_get('default_socket_timeout');
  254. curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,$timeout);
  255. curl_setopt($curl,CURLOPT_TIMEOUT,$timeout);
  256. $headers=array();
  257. curl_setopt($curl,CURLOPT_HEADERFUNCTION,
  258. // Callback for response headers
  259. function($curl,$line) use(&$headers) {
  260. if ($trim=trim($line))
  261. $headers[]=$trim;
  262. return strlen($line);
  263. }
  264. );
  265. curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,FALSE);
  266. ob_start();
  267. curl_exec($curl);
  268. curl_close($curl);
  269. $body=ob_get_clean();
  270. return array(
  271. 'body'=>$body,
  272. 'headers'=>$headers,
  273. 'engine'=>'cURL',
  274. 'cached'=>FALSE
  275. );
  276. }
  277. /**
  278. * HTTP request via PHP stream wrapper
  279. * @return array
  280. * @param $url string
  281. * @param $options array
  282. **/
  283. protected function _stream($url,$options) {
  284. $eol="\r\n";
  285. $options['header']=implode($eol,$options['header']);
  286. $body=@file_get_contents($url,FALSE,
  287. stream_context_create(array('http'=>$options)));
  288. $headers=isset($http_response_header)?
  289. $http_response_header:array();
  290. $match=NULL;
  291. foreach ($headers as $header)
  292. if (preg_match('/Content-Encoding: (.+)/',$header,$match))
  293. break;
  294. if ($match)
  295. switch ($match[1]) {
  296. case 'gzip':
  297. $body=gzdecode($body);
  298. break;
  299. case 'deflate':
  300. $body=gzuncompress($body);
  301. break;
  302. }
  303. return array(
  304. 'body'=>$body,
  305. 'headers'=>$headers,
  306. 'engine'=>'stream',
  307. 'cached'=>FALSE
  308. );
  309. }
  310. /**
  311. * HTTP request via low-level TCP/IP socket
  312. * @return array
  313. * @param $url string
  314. * @param $options array
  315. **/
  316. protected function _socket($url,$options) {
  317. $eol="\r\n";
  318. $headers=array();
  319. $body='';
  320. $parts=parse_url($url);
  321. $empty=empty($parts['port']);
  322. if ($parts['scheme']=='https') {
  323. $parts['host']='ssl://'.$parts['host'];
  324. if ($empty)
  325. $parts['port']=443;
  326. }
  327. elseif ($empty)
  328. $parts['port']=80;
  329. if (empty($parts['path']))
  330. $parts['path']='/';
  331. if (empty($parts['query']))
  332. $parts['query']='';
  333. $socket=@fsockopen($parts['host'],$parts['port']);
  334. if (!$socket)
  335. return FALSE;
  336. stream_set_blocking($socket,TRUE);
  337. fputs($socket,$options['method'].' '.$parts['path'].
  338. ($parts['query']?('?'.$parts['query']):'').' HTTP/1.0'.$eol
  339. );
  340. fputs($socket,implode($eol,$options['header']).$eol.$eol);
  341. if (isset($options['content']))
  342. fputs($socket,$options['content'].$eol);
  343. // Get response
  344. $content='';
  345. while (!feof($socket) &&
  346. ($info=stream_get_meta_data($socket)) &&
  347. !$info['timed_out'] && $str=fgets($socket,4096))
  348. $content.=$str;
  349. fclose($socket);
  350. $html=explode($eol.$eol,$content,2);
  351. $body=isset($html[1])?$html[1]:'';
  352. $headers=array_merge($headers,$current=explode($eol,$html[0]));
  353. $match=NULL;
  354. foreach ($current as $header)
  355. if (preg_match('/Content-Encoding: (.+)/',$header,$match))
  356. break;
  357. if ($match)
  358. switch ($match[1]) {
  359. case 'gzip':
  360. $body=gzdecode($body);
  361. break;
  362. case 'deflate':
  363. $body=gzuncompress($body);
  364. break;
  365. }
  366. if ($options['follow_location'] &&
  367. preg_match('/Location: (.+?)'.preg_quote($eol).'/',
  368. $html[0],$loc)) {
  369. $options['max_redirects']--;
  370. return $this->request($loc[1],$options);
  371. }
  372. return array(
  373. 'body'=>$body,
  374. 'headers'=>$headers,
  375. 'engine'=>'socket',
  376. 'cached'=>FALSE
  377. );
  378. }
  379. /**
  380. * Specify the HTTP request engine to use; If not available,
  381. * fall back to an applicable substitute
  382. * @return string
  383. * @param $arg string
  384. **/
  385. function engine($arg='curl') {
  386. $arg=strtolower($arg);
  387. $flags=array(
  388. 'curl'=>extension_loaded('curl'),
  389. 'stream'=>ini_get('allow_url_fopen'),
  390. 'socket'=>function_exists('fsockopen')
  391. );
  392. if ($flags[$arg])
  393. return $this->wrapper=$arg;
  394. foreach ($flags as $key=>$val)
  395. if ($val)
  396. return $this->wrapper=$key;
  397. user_error(E_Request);
  398. }
  399. /**
  400. * Replace old headers with new elements
  401. * @return NULL
  402. * @param $old array
  403. * @param $new string|array
  404. **/
  405. function subst(array &$old,$new) {
  406. if (is_string($new))
  407. $new=array($new);
  408. foreach ($new as $hdr) {
  409. $old=preg_grep('/'.preg_quote(strstr($hdr,':',TRUE),'/').':.+/',
  410. $old,PREG_GREP_INVERT);
  411. array_push($old,$hdr);
  412. }
  413. }
  414. /**
  415. * Submit HTTP request; Use HTTP context options (described in
  416. * http://www.php.net/manual/en/context.http.php) if specified;
  417. * Cache the page as instructed by remote server
  418. * @return array|FALSE
  419. * @param $url string
  420. * @param $options array
  421. **/
  422. function request($url,array $options=NULL) {
  423. $fw=Base::instance();
  424. $parts=parse_url($url);
  425. if (empty($parts['scheme'])) {
  426. // Local URL
  427. $url=$fw->get('SCHEME').'://'.
  428. $fw->get('HOST').
  429. ($url[0]!='/'?($fw->get('BASE').'/'):'').$url;
  430. $parts=parse_url($url);
  431. }
  432. elseif (!preg_match('/https?/',$parts['scheme']))
  433. return FALSE;
  434. if (!is_array($options))
  435. $options=array();
  436. if (empty($options['header']))
  437. $options['header']=array();
  438. elseif (is_string($options['header']))
  439. $options['header']=array($options['header']);
  440. if (!$this->wrapper)
  441. $this->engine();
  442. if ($this->wrapper!='stream') {
  443. // PHP streams can't cope with redirects when Host header is set
  444. foreach ($options['header'] as &$header)
  445. if (preg_match('/^Host:/',$header)) {
  446. $header='Host: '.$parts['host'];
  447. unset($header);
  448. break;
  449. }
  450. $this->subst($options['header'],'Host: '.$parts['host']);
  451. }
  452. $this->subst($options['header'],
  453. array(
  454. 'Accept-Encoding: gzip,deflate',
  455. 'User-Agent: Mozilla/5.0 (compatible; '.php_uname('s').')',
  456. 'Connection: close'
  457. )
  458. );
  459. if (isset($options['content'])) {
  460. if ($options['method']=='POST')
  461. $this->subst($options['header'],
  462. 'Content-Type: application/x-www-form-urlencoded');
  463. $this->subst($options['header'],
  464. 'Content-Length: '.strlen($options['content']));
  465. }
  466. if (isset($parts['user'],$parts['pass']))
  467. $this->subst($options['header'],
  468. 'Authorization: Basic '.
  469. base64_encode($parts['user'].':'.$parts['pass'])
  470. );
  471. $options+=array(
  472. 'method'=>'GET',
  473. 'header'=>$options['header'],
  474. 'follow_location'=>TRUE,
  475. 'max_redirects'=>20,
  476. 'ignore_errors'=>FALSE
  477. );
  478. $eol="\r\n";
  479. if ($fw->get('CACHE') &&
  480. preg_match('/GET|HEAD/',$options['method'])) {
  481. $cache=Cache::instance();
  482. if ($cache->exists(
  483. $hash=$fw->hash($options['method'].' '.$url).'.url',$data)) {
  484. if (preg_match('/Last-Modified: (.+?)'.preg_quote($eol).'/',
  485. implode($eol,$data['headers']),$mod))
  486. $this->subst($options['header'],
  487. 'If-Modified-Since: '.$mod[1]);
  488. }
  489. }
  490. $result=$this->{'_'.$this->wrapper}($url,$options);
  491. if ($result && isset($cache)) {
  492. if (preg_match('/HTTP\/1\.\d 304/',
  493. implode($eol,$result['headers']))) {
  494. $result=$cache->get($hash);
  495. $result['cached']=TRUE;
  496. }
  497. elseif (preg_match('/Cache-Control: max-age=(.+?)'.
  498. preg_quote($eol).'/',implode($eol,$result['headers']),$exp))
  499. $cache->set($hash,$result,$exp[1]);
  500. }
  501. return $result;
  502. }
  503. /**
  504. * Strip Javascript/CSS files of extraneous whitespaces and comments;
  505. * Return combined output as a minified string
  506. * @return string
  507. * @param $files string|array
  508. * @param $mime string
  509. * @param $header bool
  510. * @param $path string
  511. **/
  512. function minify($files,$mime=NULL,$header=TRUE,$path='') {
  513. $fw=Base::instance();
  514. if (is_string($files))
  515. $files=$fw->split($files);
  516. if (!$mime)
  517. $mime=$this->mime($files[0]);
  518. preg_match('/\w+$/',$files[0],$ext);
  519. $cache=Cache::instance();
  520. $dst='';
  521. foreach ($fw->split($path?:$fw->get('UI').';./') as $dir)
  522. foreach ($files as $file)
  523. if (is_file($save=$fw->fixslashes($dir.$file))) {
  524. if ($fw->get('CACHE') &&
  525. ($cached=$cache->exists(
  526. $hash=$fw->hash($save).'.'.$ext[0],$data)) &&
  527. $cached[0]>filemtime($save))
  528. $dst.=$data;
  529. else {
  530. $data='';
  531. $src=$fw->read($save);
  532. for ($ptr=0,$len=strlen($src);$ptr<$len;) {
  533. if (preg_match('/^@import\h+url'.
  534. '\(\h*([\'"])(.+?)\1\h*\)[^;]*;/',
  535. substr($src,$ptr),$parts)) {
  536. $path=dirname($file);
  537. $data.=$this->minify(
  538. ($path?($path.'/'):'').$parts[2],
  539. $mime,$header
  540. );
  541. $ptr+=strlen($parts[0]);
  542. continue;
  543. }
  544. if ($src[$ptr]=='/') {
  545. if ($src[$ptr+1]=='*') {
  546. // Multiline comment
  547. $str=strstr(
  548. substr($src,$ptr+2),'*/',TRUE);
  549. $ptr+=strlen($str)+4;
  550. }
  551. elseif ($src[$ptr+1]=='/') {
  552. // Single-line comment
  553. $str=strstr(
  554. substr($src,$ptr+2),"\n",TRUE);
  555. $ptr+=strlen($str)+2;
  556. }
  557. else {
  558. // Presume it's a regex pattern
  559. $regex=TRUE;
  560. // Backtrack and validate
  561. for ($ofs=$ptr;$ofs;$ofs--) {
  562. // Pattern should be preceded by
  563. // open parenthesis, colon,
  564. // object property or operator
  565. if (preg_match(
  566. '/(return|[(:=!+\-*&|])$/',
  567. substr($src,0,$ofs))) {
  568. $data.='/';
  569. $ptr++;
  570. while ($ptr<$len) {
  571. $data.=$src[$ptr];
  572. $ptr++;
  573. if ($src[$ptr-1]=='\\') {
  574. $data.=$src[$ptr];
  575. $ptr++;
  576. }
  577. elseif ($src[$ptr-1]=='/')
  578. break;
  579. }
  580. break;
  581. }
  582. elseif (!ctype_space($src[$ofs-1])) {
  583. // Not a regex pattern
  584. $regex=FALSE;
  585. break;
  586. }
  587. }
  588. if (!$regex) {
  589. // Division operator
  590. $data.=$src[$ptr];
  591. $ptr++;
  592. }
  593. }
  594. continue;
  595. }
  596. if (in_array($src[$ptr],array('\'','"'))) {
  597. $match=$src[$ptr];
  598. $data.=$match;
  599. $ptr++;
  600. // String literal
  601. while ($ptr<$len) {
  602. $data.=$src[$ptr];
  603. $ptr++;
  604. if ($src[$ptr-1]=='\\') {
  605. $data.=$src[$ptr];
  606. $ptr++;
  607. }
  608. elseif ($src[$ptr-1]==$match)
  609. break;
  610. }
  611. continue;
  612. }
  613. if (ctype_space($src[$ptr])) {
  614. if ($ptr+1<strlen($src) &&
  615. preg_match('/[\w'.($ext[0]=='css'?
  616. '#\.+\-*()\[\]':'\$').']{2}|'.
  617. '[+\-]{2}/',
  618. substr($data,-1).$src[$ptr+1]))
  619. $data.=' ';
  620. $ptr++;
  621. continue;
  622. }
  623. $data.=$src[$ptr];
  624. $ptr++;
  625. }
  626. if ($fw->get('CACHE'))
  627. $cache->set($hash,$data);
  628. $dst.=$data;
  629. }
  630. }
  631. if (PHP_SAPI!='cli' && $header)
  632. header('Content-Type: '.$mime.'; charset='.$fw->get('ENCODING'));
  633. return $dst;
  634. }
  635. /**
  636. * Retrieve RSS feed and return as an array
  637. * @return array|FALSE
  638. * @param $url string
  639. * @param $max int
  640. * @param $tags string
  641. **/
  642. function rss($url,$max=10,$tags=NULL) {
  643. if (!$data=$this->request($url))
  644. return FALSE;
  645. // Suppress errors caused by invalid XML structures
  646. libxml_use_internal_errors(TRUE);
  647. $xml=simplexml_load_string($data['body'],
  648. NULL,LIBXML_NOBLANKS|LIBXML_NOERROR);
  649. if (!is_object($xml))
  650. return FALSE;
  651. $out=array();
  652. if (isset($xml->channel)) {
  653. $out['source']=(string)$xml->channel->title;
  654. $max=min($max,count($xml->channel->item));
  655. for ($i=0;$i<$max;$i++) {
  656. $item=$xml->channel->item[$i];
  657. $list=array(''=>NULL)+$item->getnamespaces(TRUE);
  658. $fields=array();
  659. foreach ($list as $ns=>$uri)
  660. foreach ($item->children($uri) as $key=>$val)
  661. $fields[$ns.($ns?':':'').$key]=(string)$val;
  662. $out['feed'][]=$fields;
  663. }
  664. }
  665. else
  666. return FALSE;
  667. Base::instance()->scrub($out,$tags);
  668. return $out;
  669. }
  670. /**
  671. * Retrieve information from whois server
  672. * @return string|FALSE
  673. * @param $addr string
  674. * @param $server string
  675. **/
  676. function whois($addr,$server='whois.internic.net') {
  677. $socket=@fsockopen($server,43,$errno,$errstr);
  678. if (!$socket)
  679. // Can't establish connection
  680. return FALSE;
  681. // Set connection timeout parameters
  682. stream_set_blocking($socket,TRUE);
  683. stream_set_timeout($socket,ini_get('default_socket_timeout'));
  684. // Send request
  685. fputs($socket,$addr."\r\n");
  686. $info=stream_get_meta_data($socket);
  687. // Get response
  688. $response='';
  689. while (!feof($socket) && !$info['timed_out']) {
  690. $response.=fgets($socket,4096); // MDFK97
  691. $info=stream_get_meta_data($socket);
  692. }
  693. fclose($socket);
  694. return $info['timed_out']?FALSE:trim($response);
  695. }
  696. /**
  697. * Return a URL/filesystem-friendly version of string
  698. * @return string
  699. * @param $text string
  700. **/
  701. function slug($text) {
  702. return trim(strtolower(preg_replace('/([^\pL\pN])+/u','-',
  703. trim(strtr(str_replace('\'','',$text),
  704. array(
  705. 'Ǎ'=>'A','А'=>'A','Ā'=>'A','Ă'=>'A','Ą'=>'A','Å'=>'A',
  706. 'Ǻ'=>'A','Ä'=>'Ae','Á'=>'A','À'=>'A','Ã'=>'A','Â'=>'A',
  707. 'Æ'=>'AE','Ǽ'=>'AE','Б'=>'B','Ç'=>'C','Ć'=>'C','Ĉ'=>'C',
  708. 'Č'=>'C','Ċ'=>'C','Ц'=>'C','Ч'=>'Ch','Ð'=>'Dj','Đ'=>'Dj',
  709. 'Ď'=>'Dj','Д'=>'Dj','É'=>'E','Ę'=>'E','Ё'=>'E','Ė'=>'E',
  710. 'Ê'=>'E','Ě'=>'E','Ē'=>'E','È'=>'E','Е'=>'E','Э'=>'E',
  711. 'Ë'=>'E','Ĕ'=>'E','Ф'=>'F','Г'=>'G','Ģ'=>'G','Ġ'=>'G',
  712. 'Ĝ'=>'G','Ğ'=>'G','Х'=>'H','Ĥ'=>'H','Ħ'=>'H','Ï'=>'I',
  713. 'Ĭ'=>'I','İ'=>'I','Į'=>'I','Ī'=>'I','Í'=>'I','Ì'=>'I',
  714. 'И'=>'I','Ǐ'=>'I','Ĩ'=>'I','Î'=>'I','IJ'=>'IJ','Ĵ'=>'J',
  715. 'Й'=>'J','Я'=>'Ja','Ю'=>'Ju','К'=>'K','Ķ'=>'K','Ĺ'=>'L',
  716. 'Л'=>'L','Ł'=>'L','Ŀ'=>'L','Ļ'=>'L','Ľ'=>'L','М'=>'M',
  717. 'Н'=>'N','Ń'=>'N','Ñ'=>'N','Ņ'=>'N','Ň'=>'N','Ō'=>'O',
  718. 'О'=>'O','Ǿ'=>'O','Ǒ'=>'O','Ơ'=>'O','Ŏ'=>'O','Ő'=>'O',
  719. 'Ø'=>'O','Ö'=>'Oe','Õ'=>'O','Ó'=>'O','Ò'=>'O','Ô'=>'O',
  720. 'Œ'=>'OE','П'=>'P','Ŗ'=>'R','Р'=>'R','Ř'=>'R','Ŕ'=>'R',
  721. 'Ŝ'=>'S','Ş'=>'S','Š'=>'S','Ș'=>'S','Ś'=>'S','С'=>'S',
  722. 'Ш'=>'Sh','Щ'=>'Shch','Ť'=>'T','Ŧ'=>'T','Ţ'=>'T','Ț'=>'T',
  723. 'Т'=>'T','Ů'=>'U','Ű'=>'U','Ŭ'=>'U','Ũ'=>'U','Ų'=>'U',
  724. 'Ū'=>'U','Ǜ'=>'U','Ǚ'=>'U','Ù'=>'U','Ú'=>'U','Ü'=>'Ue',
  725. 'Ǘ'=>'U','Ǖ'=>'U','У'=>'U','Ư'=>'U','Ǔ'=>'U','Û'=>'U',
  726. 'В'=>'V','Ŵ'=>'W','Ы'=>'Y','Ŷ'=>'Y','Ý'=>'Y','Ÿ'=>'Y',
  727. 'Ź'=>'Z','З'=>'Z','Ż'=>'Z','Ž'=>'Z','Ж'=>'Zh','á'=>'a',
  728. 'ă'=>'a','â'=>'a','à'=>'a','ā'=>'a','ǻ'=>'a','å'=>'a',
  729. 'ä'=>'ae','ą'=>'a','ǎ'=>'a','ã'=>'a','а'=>'a','ª'=>'a',
  730. 'æ'=>'ae','ǽ'=>'ae','б'=>'b','č'=>'c','ç'=>'c','ц'=>'c',
  731. 'ċ'=>'c','ĉ'=>'c','ć'=>'c','ч'=>'ch','ð'=>'dj','ď'=>'dj',
  732. 'д'=>'dj','đ'=>'dj','э'=>'e','é'=>'e','ё'=>'e','ë'=>'e',
  733. 'ê'=>'e','е'=>'e','ĕ'=>'e','è'=>'e','ę'=>'e','ě'=>'e',
  734. 'ė'=>'e','ē'=>'e','ƒ'=>'f','ф'=>'f','ġ'=>'g','ĝ'=>'g',
  735. 'ğ'=>'g','г'=>'g','ģ'=>'g','х'=>'h','ĥ'=>'h','ħ'=>'h',
  736. 'ǐ'=>'i','ĭ'=>'i','и'=>'i','ī'=>'i','ĩ'=>'i','į'=>'i',
  737. 'ı'=>'i','ì'=>'i','î'=>'i','í'=>'i','ï'=>'i','ij'=>'ij',
  738. 'ĵ'=>'j','й'=>'j','я'=>'ja','ю'=>'ju','ķ'=>'k','к'=>'k',
  739. 'ľ'=>'l','ł'=>'l','ŀ'=>'l','ĺ'=>'l','ļ'=>'l','л'=>'l',
  740. 'м'=>'m','ņ'=>'n','ñ'=>'n','ń'=>'n','н'=>'n','ň'=>'n',
  741. 'ʼn'=>'n','ó'=>'o','ò'=>'o','ǒ'=>'o','ő'=>'o','о'=>'o',
  742. 'ō'=>'o','º'=>'o','ơ'=>'o','ŏ'=>'o','ô'=>'o','ö'=>'oe',
  743. 'õ'=>'o','ø'=>'o','ǿ'=>'o','œ'=>'oe','п'=>'p','р'=>'r',
  744. 'ř'=>'r','ŕ'=>'r','ŗ'=>'r','ſ'=>'s','ŝ'=>'s','ș'=>'s',
  745. 'š'=>'s','ś'=>'s','с'=>'s','ş'=>'s','ш'=>'sh','щ'=>'shch',
  746. 'ß'=>'ss','ţ'=>'t','т'=>'t','ŧ'=>'t','ť'=>'t','ț'=>'t',
  747. 'у'=>'u','ǘ'=>'u','ŭ'=>'u','û'=>'u','ú'=>'u','ų'=>'u',
  748. 'ù'=>'u','ű'=>'u','ů'=>'u','ư'=>'u','ū'=>'u','ǚ'=>'u',
  749. 'ǜ'=>'u','ǔ'=>'u','ǖ'=>'u','ũ'=>'u','ü'=>'ue','в'=>'v',
  750. 'ŵ'=>'w','ы'=>'y','ÿ'=>'y','ý'=>'y','ŷ'=>'y','ź'=>'z',
  751. 'ž'=>'z','з'=>'z','ż'=>'z','ж'=>'zh'
  752. )+Base::instance()->get('DIACRITICS'))))),'-');
  753. }
  754. /**
  755. * Return chunk of text from standard Lorem Ipsum passage
  756. * @return string
  757. * @param $count int
  758. * @param $max int
  759. * @param $std bool
  760. **/
  761. function filler($count=1,$max=20,$std=TRUE) {
  762. $out='';
  763. if ($std)
  764. $out='Lorem ipsum dolor sit amet, consectetur adipisicing elit, '.
  765. 'sed do eiusmod tempor incididunt ut labore et dolore magna '.
  766. 'aliqua.';
  767. $rnd=explode(' ',
  768. 'a ab ad accusamus adipisci alias aliquam amet animi aperiam '.
  769. 'architecto asperiores aspernatur assumenda at atque aut beatae '.
  770. 'blanditiis cillum commodi consequatur corporis corrupti culpa '.
  771. 'cum cupiditate debitis delectus deleniti deserunt dicta '.
  772. 'dignissimos distinctio dolor ducimus duis ea eaque earum eius '.
  773. 'eligendi enim eos error esse est eum eveniet ex excepteur '.
  774. 'exercitationem expedita explicabo facere facilis fugiat harum '.
  775. 'hic id illum impedit in incidunt ipsa iste itaque iure iusto '.
  776. 'laborum laudantium libero magnam maiores maxime minim minus '.
  777. 'modi molestiae mollitia nam natus necessitatibus nemo neque '.
  778. 'nesciunt nihil nisi nobis non nostrum nulla numquam occaecati '.
  779. 'odio officia omnis optio pariatur perferendis perspiciatis '.
  780. 'placeat porro possimus praesentium proident quae quia quibus '.
  781. 'quo ratione recusandae reiciendis rem repellat reprehenderit '.
  782. 'repudiandae rerum saepe sapiente sequi similique sint soluta '.
  783. 'suscipit tempora tenetur totam ut ullam unde vel veniam vero '.
  784. 'vitae voluptas');
  785. for ($i=0,$add=$count-(int)$std;$i<$add;$i++) {
  786. shuffle($rnd);
  787. $words=array_slice($rnd,0,mt_rand(3,$max));
  788. $out.=' '.ucfirst(implode(' ',$words)).'.';
  789. }
  790. return $out;
  791. }
  792. }
  793. if (!function_exists('gzdecode')) {
  794. /**
  795. * Decode gzip-compressed string
  796. * @param $str string
  797. **/
  798. function gzdecode($str) {
  799. $fw=Base::instance();
  800. if (!is_dir($tmp=$fw->get('TEMP')))
  801. mkdir($tmp,Base::MODE,TRUE);
  802. file_put_contents($file=$tmp.'/'.
  803. $fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
  804. $fw->hash(uniqid(NULL,TRUE)).'.gz',$str,LOCK_EX);
  805. ob_start();
  806. readgzfile($file);
  807. $out=ob_get_clean();
  808. @unlink($file);
  809. return $out;
  810. }
  811. }