Automad
 All Classes Functions Variables Pages
parse.php
1 <?php
2 /*
3  * ....
4  * .: '':.
5  * :::: ':..
6  * ::. ''..
7  * .:'.. ..':.:::' . :. '':.
8  * :. '' '' '. ::::.. ..:
9  * ::::. ..':.. .'''::::: .
10  * :::::::.. '..:::: :. :::: :
11  * ::'':::::::. ':::.'':.:::: :
12  * :.. ''::::::....': '':: :
13  * :::::. '::::: : .. '' .
14  * .''::::::::... ':::.'' ..'' :.''''.
15  * :..:::''::::: :::::...:'' :..:
16  * ::::::. ':::: :::::::: ..:: .
17  * ::::::::.:::: :::::::: :'':.:: .''
18  * ::: '::::::::.' ''::::: :.' '': :
19  * ::: :::::::::..' :::: ::...' .
20  * ::: .:::::::::: :::: :::: .:'
21  * '::' ''::::::: :::: : :: :
22  * ':::: :::: :'' .:
23  * :::: :::: ..''
24  * :::: ..:::: .:''
25  * '''' '''''
26  *
27  *
28  * AUTOMAD
29  *
30  * Copyright (c) 2014 by Marc Anton Dahmen
31  * http://marcdahmen.de
32  *
33  * Licensed under the MIT license.
34  * http://automad.org/license
35  */
36 
37 
38 namespace Automad\Core;
39 
40 
41 defined('AUTOMAD') or die('Direct access not permitted!');
42 
43 
52 class Parse {
53 
54 
61  public static function allowedFileTypes() {
62 
63  // Split string
64  $types = explode(AM_PARSE_STR_SEPARATOR, AM_ALLOWED_FILE_TYPES);
65  // Trim items
66  $types = array_map(function($type) {
67  return trim($type);
68  }, $types);
69 
70  return $types;
71 
72  }
73 
74 
82  public static function extractTags($data) {
83 
84  $tags = array();
85 
86  foreach ($data as $key => $value) {
87 
88  if ($key == AM_KEY_TAGS) {
89 
90  // All tags are splitted into an array
91  $tags = explode(AM_PARSE_STR_SEPARATOR, $value);
92  // Trim & strip tags
93  $tags = array_map(function($tag) {
94  return trim(strip_tags($tag));
95  }, $tags);
96 
97  }
98 
99  }
100 
101  return $tags;
102 
103  }
104 
105 
116  public static function fileDeclaration($str, $Page, $stripBaseDir = false) {
117 
118  $files = array();
119 
120  foreach (explode(AM_PARSE_STR_SEPARATOR, $str) as $glob) {
121 
122  if ($f = glob(Resolve::filePath($Page->path, trim($glob)))) {
123  $files = array_merge($files, $f);
124  }
125 
126  }
127 
128  array_walk($files, function(&$file) use ($stripBaseDir) {
129 
130  $file = realpath($file);
131 
132  if ($stripBaseDir) {
133  $file = str_replace(AM_BASE_DIR, '', $file);
134  }
135 
136  });
137 
138  return $files;
139 
140  }
141 
142 
154  public static function isFileName($str) {
155 
156  // Remove possible query string
157  $str = preg_replace('/\?.*/', '', $str);
158 
159  // Get just the basename
160  $str = basename($str);
161 
162  // Possible extension
163  $extension = strtolower(pathinfo($str, PATHINFO_EXTENSION));
164 
165  if (in_array($extension, self::allowedFileTypes())) {
166  return true;
167  } else {
168  return false;
169  }
170 
171  }
172 
173 
181  public static function jsonEscape($str) {
182 
183  $search = array('"', "'", "\n", "\r");
184  $replace = array('\"', "\'", ' ', ' ');
185 
186  return str_replace($search, $replace, $str);
187 
188  }
189 
190 
198  public static function jsonOptions($str) {
199 
200  $options = array();
201 
202  if ($str) {
203 
204  $debug['String'] = $str;
205 
206  // Clean up "dirty" JSON by replacing single with double quotes and
207  // wrapping all keys in double quotes.
208  $str = str_replace("'", '"', $str);
209  $str = preg_replace('/([{,]+)\s*([^":\s]+)\s*:/i', '\1"\2":', $str);
210 
211  // Decode JSON.
212  $options = json_decode($str, true);
213 
214  // Remove all undefined items (empty string).
215  // It is not possible to use array_filter($options, 'strlen') here, since an array item could be an array itself and strlen() only expects strings.
216  $options = array_filter($options, function($value) {
217  return ($value !== '');
218  });
219 
220  $debug['JSON'] = $options;
221  Debug::log($debug);
222 
223  }
224 
225  return $options;
226 
227  }
228 
229 
241  public static function markdownFile($file) {
242 
243  $vars = self::textFile($file);
244 
245  $vars = array_map(function($var) {
246 
247  $regexTag = '/^<(!--|base|link|meta|script|style|title).*>$/is';
248 
249  if (strpos($var, "\n") !== false && !preg_match($regexTag, $var)) {
250  // If $var is a multiline string and not only one or more tags (meta, script, link tags ...).
251  return \Michelf\MarkdownExtra::defaultTransform($var);
252  } else {
253  // If $var is just a single line or just one or more <head> element(s), skip parsing.
254  return $var;
255  }
256 
257  }, $vars);
258 
259  return $vars;
260 
261  }
262 
263 
270  public static function queryArray() {
271 
272  // First get existing query string to prevent overwriting existing settings passed already
273  // and store its data in $query.
274  if (isset($_GET)) {
275  $query = $_GET;
276  } else {
277  $query = array();
278  }
279 
280  return $query;
281 
282  }
283 
284 
294  public static function queryKey($key) {
295 
296  if (isset($_GET[$key])) {
297  $queryKey = $_GET[$key];
298  } else {
299  $queryKey = '';
300  }
301 
302  return $queryKey;
303 
304  }
305 
306 
313  public static function request() {
314 
315  $request = '';
316 
317  if (!isset($_SERVER['QUERY_STRING'])) {
318  $_SERVER['QUERY_STRING'] = '';
319  }
320 
321  // Check if the query string starts with a '/'.
322  // That is the case if the requested page gets passed as part of the query string when rewriting is enabled (.htaccess or nginx.conf).
323  if (strncmp($_SERVER['QUERY_STRING'], '/', 1) === 0) {
324 
325  // The requested page gets passed as part of the query string when pretty URLs are enabled and requests get rewritten like:
326  // domain.com/page -> domain.com/index.php?/page
327  // Or with a query string:
328  // domain.com/page?key=value -> domain.com/index.php?/page&key=value
329  // Depending on the rewrite rules and environment, the query string can also look like:
330  // domain.com/page?key=vaule -> domain.com/index.php?/page?key=value (note the 2nd "?"!)
331  $query = preg_split('/[&\?]/', $_SERVER['QUERY_STRING'], 2);
332  $request = $query[0];
333  Debug::log($query, 'Getting request from QUERY_STRING "' . $_SERVER['QUERY_STRING'] . '"');
334 
335  // In case there is no real query string except the requested page.
336  if (!isset($query[1])) {
337  $query[1] = '';
338  }
339 
340  // Rebuild correct $_GET array without requested page.
341  parse_str($query[1], $_GET);
342 
343  // Remove request from QUERY_STRING.
344  $_SERVER['QUERY_STRING'] = $query[1];
345 
346  Debug::log($_GET, '$_GET');
347 
348  } else {
349 
350  // The requested page gets passed 'index.php/page/path'.
351  // That can be the case if rewriting is disabled and AM_INDEX equals '/index.php'.
352  if (isset($_SERVER['PATH_INFO'])) {
353 
354  $request = $_SERVER['PATH_INFO'];
355  Debug::log('Getting request from PATH_INFO');
356 
357  } else if (isset($_SERVER['ORIG_PATH_INFO'])) {
358 
359  $request = $_SERVER['ORIG_PATH_INFO'];
360  Debug::log('Getting request from ORIG_PATH_INFO');
361 
362  } else if (isset($_SERVER['REQUEST_URI'])) {
363 
364  $request = trim(str_replace($_SERVER['QUERY_STRING'], '', $_SERVER['REQUEST_URI']), '?');
365  Debug::log('Getting request from REQUEST_URI');
366 
367  } else if (isset($_SERVER['REDIRECT_URL'])) {
368 
369  $request = $_SERVER['REDIRECT_URL'];
370  Debug::log('Getting request from REDIRECT_URL');
371 
372  } else if (isset($_SERVER['PHP_SELF'])) {
373 
374  $request = $_SERVER['PHP_SELF'];
375  Debug::log('Getting request from PHP_SELF');
376 
377  }
378 
379  }
380 
381  // Remove unwanted components from the request.
382  $request = str_replace(AM_BASE_URL, '', $request);
383  $request = str_replace('/index.php', '', $request);
384 
385  // Remove trailing slash from URL to keep relative links consistent.
386  if (substr($request, -1) == '/' && $request != '/') {
387  header('Location: ' . AM_BASE_URL . AM_INDEX . rtrim($request, '/'), false, 301);
388  die;
389  }
390 
391  $request = '/' . trim($request, '/');
392 
393  Debug::log($request, 'Requested page');
394 
395  return $request;
396 
397  }
398 
399 
413  public static function sanitize($str, $removeDots = false) {
414 
415  // If dots should be removed from $str, replace them with '-', since URLify::filter() only removes them fully without replacing.
416  if ($removeDots) {
417  $str = str_replace('.', '-', $str);
418  }
419 
420  // Convert slashes separately to avoid issues with regex in URLify.
421  $str = str_replace('/', '-', $str);
422 
423  // Configure URLify.
424  // Add non-word chars and reset the remove list.
425  // Note: $maps gets directly manipulated without using URLify::add_chars().
426  // Using the add_chars() method would extend $maps every time, Parse::sanitize() gets called.
427  // Adding a new array to $maps using a key avoids that and just overwrites that same array after the first call without adding new elements.
428  \JBroadway\URLify::$maps['nonWordChars'] = array('=' => '-', '&' => '-and-', '+' => '-plus-', '@' => '-at-', '|' => '-', '*' => '-x-');
429  \JBroadway\URLify::$remove_list = array();
430 
431  // Since all possible dots got removed above (if $removeDots is true),
432  // $str should be filtered as filename to keep dots if there are still in $str.
433  return \JBroadway\URLify::filter($str, 100, '', true);
434 
435  }
436 
437 
447  public static function siteData() {
448 
449  // Define default settings.
450  // Use the server name as default site name and the first found theme folder as default theme.
451  $themes = glob(AM_BASE_DIR . AM_DIR_THEMES . '/*', GLOB_ONLYDIR);
452  $defaults = array(
453  AM_KEY_SITENAME => $_SERVER['SERVER_NAME'],
454  AM_KEY_THEME => basename(reset($themes))
455  );
456 
457  // Merge defaults with settings from file.
458  return array_merge($defaults, self::markdownFile(AM_FILE_SITE_SETTINGS));
459 
460  }
461 
462 
473  public static function textFile($file) {
474 
475  $vars = array();
476 
477  // Get file content and normalize line breaks.
478  $content = preg_replace('/\r\n?/', "\n", file_get_contents($file));
479 
480  // Split $content into data blocks on every line only containing one or more AM_PARSE_BLOCK_SEPARATOR and whitespace, followed by a key in a new line.
481  $pairs = preg_split('/\n' . preg_quote(AM_PARSE_BLOCK_SEPARATOR) . '+\s*\n(?=' . AM_CHARCLASS_VAR_CONTENT . '+' . preg_quote(AM_PARSE_PAIR_SEPARATOR) . ')/s', $content, NULL, PREG_SPLIT_NO_EMPTY);
482 
483  // Split $pairs into an array of vars.
484  foreach ($pairs as $pair) {
485 
486  list($key, $value) = explode(AM_PARSE_PAIR_SEPARATOR, $pair, 2);
487  $vars[trim($key)] = trim($value);
488 
489  }
490 
491  // Remove undefined (empty) items.
492  $vars = array_filter($vars, 'strlen');
493 
494  return $vars;
495 
496  }
497 
498 
499 }
500 
501 
502 ?>
static allowedFileTypes()
Definition: parse.php:61
static log($element, $description= '')
Definition: debug.php:113
static fileDeclaration($str, $Page, $stripBaseDir=false)
Definition: parse.php:116
static siteData()
Definition: parse.php:447
static isFileName($str)
Definition: parse.php:154
static request()
Definition: parse.php:313
static textFile($file)
Definition: parse.php:473
static jsonOptions($str)
Definition: parse.php:198
static filePath($pagePath, $filePath)
Definition: resolve.php:72
static sanitize($str, $removeDots=false)
Definition: parse.php:413
static jsonEscape($str)
Definition: parse.php:181
static queryKey($key)
Definition: parse.php:294
static queryArray()
Definition: parse.php:270
static markdownFile($file)
Definition: parse.php:241
static extractTags($data)
Definition: parse.php:82