Highway to PSR2
[openemr.git] / library / core / src / Header.php
blob5a9f095a814f41cbee2ffeb6c870f95001c2d5ad
1 <?php
2 /**
3 * OpenEMR <http://open-emr.org>.
5 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
6 */
8 namespace OpenEMR\Core;
10 use Symfony\Component\Yaml\Yaml;
11 use Symfony\Component\Yaml\Exception\ParseException;
13 /**
14 * Class Header.
16 * Helper class to generate some `<script>` and `<link>` elements based on a
17 * configuration file. This file would be a good place to include other helpers
18 * for creating a `<head>` element, but for now it sufficently handles the
19 * `setupHeader()`
21 * @package OpenEMR
22 * @subpackage Core
23 * @author Robert Down <robertdown@live.com>
24 * @copyright Copyright (c) 2017 Robert Down
26 class Header
29 /**
30 * Setup various <head> elements.
32 * See root_dir/config/config.yaml for available assets
34 * Example usage in a PHP view script:
35 * ```php
36 * // Top of script with require_once statements
37 * use OpenEMR\Core\Header;
39 * // Inside of <head>
40 * // If no special assets are needed:
41 * Header::setupHeader();
43 * // If 1 special asset is needed:
44 * Header::setupHeader('key-of-asset');
46 * // If 2 or more assets are needed:
47 * Header::setupHeader(['array', 'of', 'keys']);
48 * ```
50 * Inside of a twig template (Parameters same as before):
51 * ```html
52 * {{ includeAsset() }}
53 * ```
55 * Inside of a smarty template, use | (pipe) delimited string of key names
56 * ```php
57 * {headerTemplate}
58 * {headerTemplate assets='key-of-asset'} (1 optional assets)
59 * {headerTemplate assets='array|of|keys'} (multiple optional assets. ie. via | delimiter)
60 * ```
62 * The above example will render `<script>` tags and `<link>` tag which
63 * bring in the requested assets from config.yaml
65 * @param array|string $assets Asset(s) to include
66 * @throws ParseException If unable to parse the config file
67 * @return string
69 public static function setupHeader($assets = [])
71 try {
72 html_header_show();
73 echo self::loadTheme();
74 echo self::includeAsset($assets);
75 } catch (\InvalidArgumentException $e) {
76 error_log($e->getMessage());
80 /**
81 * Include an asset from a config file.
83 * Static function to read in a YAML file into an array, check if the
84 * $assets keys are in the config file, and from the config file generate
85 * the HTML for a `<script>` or `<link>` tag.
87 * This is a private function, use Header::setupHeader() instead
89 * @param array|string $assets Asset(s) to include
90 * @throws ParseException If unable to parse the config file
91 * @return string
93 private static function includeAsset($assets = [])
96 if (is_string($assets)) {
97 $assets = [$assets];
100 // @TODO Hard coded the path to the config file, not good RD 2017-05-27
101 $map = self::readConfigFile("{$GLOBALS['fileroot']}/config/config.yaml");
102 $scripts = [];
103 $links = [];
105 foreach ($map as $k => $opts) {
106 $autoload = (isset($opts['autoload'])) ? $opts['autoload'] : false;
107 $rtl = (isset($opts['rtl'])) ? $opts['rtl'] : false;
108 if ($autoload === true || in_array($k, $assets)) {
109 $tmp = self::buildAsset($opts);
111 foreach ($tmp['scripts'] as $s) {
112 $scripts[] = $s;
115 foreach ($tmp['links'] as $l) {
116 $links[] = $l;
119 if ($rtl && $_SESSION['language_direction'] == 'rtl') {
120 $tmpRtl = self::buildAsset($rtl);
121 foreach ($tmpRtl['scripts'] as $s) {
122 $scripts[] = $s;
125 foreach ($tmpRtl['links'] as $l) {
126 $links[] = $l;
132 $linksStr = implode("", $links);
133 $scriptsStr = implode("", $scripts);
134 return "\n{$linksStr}\n{$scriptsStr}\n";
138 * Build an html element from config options.
140 * @var array $opts Options
141 * @return array Array with `scripts` and `links` keys which contain arrays of elements
143 private static function buildAsset($opts = array())
145 $script = (isset($opts['script'])) ? $opts['script'] : false;
146 $link = (isset($opts['link'])) ? $opts['link'] : false;
147 $basePath = self::parsePlaceholders($opts['basePath']);
149 $scripts = [];
150 $links = [];
152 if ($script) {
153 $script = self::parsePlaceholders($script);
154 $path = self::createFullPath($basePath, $script);
155 $scripts[] = self::createElement($path, 'script');
158 if ($link) {
159 if (!is_string($link) && !is_array($link)) {
160 throw new \InvalidArgumentException("Link must be of type string or array");
163 if (is_string($link)) {
164 $link = [$link];
167 foreach ($link as $l) {
168 $l = self::parsePlaceholders($l);
169 $path = self::createFullPath($basePath, $l);
170 $links[] = self::createElement($path, 'link');
174 return ['scripts' => $scripts, 'links' => $links];
177 private static function loadTheme()
179 $link = '<link rel="stylesheet" href="%css_header%" type="text/css">';
180 return self::parsePlaceholders($link);
184 * Parse a string for $GLOBAL key placeholders %key-name%.
186 * Perform a regex match all in the given subject for anything warpped in
187 * percent signs `%some-key%` and if that string exists in the $GLOBALS
188 * array, will replace the occurence with the value of that key.
190 * @param string $subject String containing placeholders (%key-name%)
191 * @return string The new string with properly replaced keys
193 public static function parsePlaceholders($subject)
195 $re = '/%(.*)%/';
196 $matches = [];
197 preg_match_all($re, $subject, $matches, PREG_SET_ORDER, 0);
199 foreach ($matches as $match) {
200 if (array_key_exists($match[1], $GLOBALS)) {
201 $subject = str_replace($match[0], $GLOBALS["{$match[1]}"], $subject);
205 return $subject;
209 * Create the actual HTML element.
211 * @param string $path File path to load
212 * @param string $type Must be `script` or `link`
213 * @return string mixed HTML element
215 private static function createElement($path, $type)
218 $script = "<script type=\"text/javascript\" src=\"%path%\"></script>\n";
219 $link = "<link rel=\"stylesheet\" href=\"%path%\" type=\"text/css\">\n";
221 $template = ($type == 'script') ? $script : $link;
222 $v = $GLOBALS['v_js_includes'];
223 $path = $path . "?v={$v}";
224 return str_replace("%path%", $path, $template);
228 * Create a full path from given parts.
230 * @param string $base Base path
231 * @param string $path specific path / filename
232 * @return string The full path
234 private static function createFullPath($base, $path)
236 return $base . $path;
240 * Read a config file and turn it into an array.
242 * @param string $file Full path to filename
243 * @return array Array of assets
245 private static function readConfigFile($file)
247 try {
248 $config = Yaml::parse(file_get_contents($file));
249 return $config['assets'];
250 } catch (ParseException $e) {
251 error_log($e->getMessage());
252 // @TODO need to handle this better. RD 2017-05-24