This implements a very simple style rewriter, it works but i still have to check...
[shindig.git] / php / src / gadgets / rewrite / ContentRewriter.php
blob39750411a7ae32077b441cde524d99dfed053609
1 <?php
3 /**
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
22 /**
23 * Implements the Content-Rewrite feature which rewrites all image, css and script
24 * links to their proxied versions, which can be quite a latency improvement, and
25 * save the gadget dev's server from melting down
28 class ContentRewriter extends DomRewriter {
29 private $rewrite;
30 private $baseUrl;
31 private $defaultRewrite = array('include-url' => array('*'), 'exclude-url' => array(), 'refresh' => '86400');
33 public function __construct(GadgetContext $context, Gadget &$gadget) {
34 parent::__construct($context, $gadget);
35 // if no rewrite params are set in the gadget but rewrite_by_default is on, use our default rules (rewrite all)
36 if (! isset($gadget->gadgetSpec->rewrite) && Config::get('rewrite_by_default')) {
37 $this->rewrite = $this->defaultRewrite;
38 } else {
39 $this->rewrite = $gadget->gadgetSpec->rewrite;
41 // the base url of the gadget is used for relative paths
42 $this->baseUrl = substr($this->context->getUrl(), 0, strrpos($this->context->getUrl(), '/') + 1);
45 /**
46 * Register our dom node observers
48 * @param GadgetRewriter $gadgetRewriter
50 public function register(GadgetRewriter &$gadgetRewriter) {
51 $gadgetRewriter->addObserver('img', $this, 'rewriteImage');
52 $gadgetRewriter->addObserver('style', $this, 'rewriteStyle');
53 $gadgetRewriter->addObserver('script', $this, 'rewriteScript');
54 $gadgetRewriter->addObserver('link', $this, 'rewriteStyleLink');
57 /**
58 * Produces the proxied version of a URL if it falls within the content-rewrite params and
59 * will append a refresh param to the proxied url based on the expires param, and the gadget
60 * url so that the proxy server knows to rewrite it's content or not
62 * @param string $url
64 private function getProxyUrl($url) {
65 if (strpos(strtolower($url), 'http://') === false && strpos(strtolower($url), 'https://') === false) {
66 $url = $this->baseUrl . $url;
68 $url = Config::get('web_prefix') . '/gadgets/proxy?url=' . urlencode($url);
69 $url .= '&refresh=' . (isset($this->rewrite['expires']) && is_numeric($this->rewrite['expires']) ? $this->rewrite['expires'] : '3600');
70 $url .= '&gadget=' . urlencode($this->context->getUrl());
71 return $url;
74 /**
75 * Checks the URL against the include-url and exclude-url params
77 * @param string $url
79 private function includedUrl($url) {
80 $included = $excluded = false;
81 if (isset($this->rewrite['include-url'])) {
82 foreach ($this->rewrite['include-url'] as $includeUrl) {
83 if ($includeUrl == '*' || strpos($url, $includeUrl) !== false) {
84 $included = true;
85 break;
89 if (isset($this->rewrite['exclude-url'])) {
90 foreach ($this->rewrite['exclude-url'] as $excludeUrl) {
91 if ($excludeUrl == '*' || strpos($url, $excludeUrl) !== false) {
92 $excluded = true;
93 break;
97 return ($included && ! $excluded);
101 * Rewrites the src attribute of an img tag
103 * @param DOMElement $node
105 public function rewriteImage(DOMElement &$node) {
106 if (($src = $node->getAttribute('src')) != null && $this->includedUrl($src)) {
107 $node->setAttribute('src', $this->getProxyUrl($src));
112 * Tries to find url(<url tag>) constructs and rewrite them to their
113 * proxied counterparts
115 * @param DOMElement $node
117 public function rewriteStyle(DOMElement &$node) {
118 $content = $node->nodeValue;
119 $newVal = '';
120 // loop through the url elements in the content
121 while (($pos = strpos($content, 'url')) !== false) {
122 // output everything before this url tag
123 $newVal .= substr($content, 0, $pos + 3);
124 $content = substr($content, $pos + 3);
125 // low tech protection against miss-reading tags, if the open ( is to far away, this is probabbly a miss-read
126 if (($beginTag = strpos($content, '(')) < 4) {
127 $content = substr($content, $beginTag + 1);
128 $endTag = strpos($content, ')');
129 $tag = str_replace(array("'", "\""), '', trim(substr($content, 0, $endTag)));
130 // at this point $tag should be the actual url aka: http://example.org/bar/foo.gif
131 if ($this->includedUrl($tag)) {
132 $newVal .= "('" . $this->getProxyUrl($tag) . "')";
133 } else {
134 $newVal .= "('$tag')";
136 $content = substr($content, $endTag + 1);
139 // append what's left
140 $newVal .= $content;
141 $node->nodeValue = $newVal;
144 public function rewriteScript(DOMElement &$node) {
145 if (($src = $node->getAttribute('src')) != null && $this->includedUrl($src)) {
146 // make sure not to rewrite our forcedJsLibs src tag, else things break
147 if (strpos($src, '/gadgets/js') === false) {
148 $node->setAttribute('src', $this->getProxyUrl($src));
153 public function rewriteStyleLink(DOMElement &$node) {
154 if (($src = $node->getAttribute('href')) != null && $this->includedUrl($src)) {
155 $node->setAttribute('href', $this->getProxyUrl($src));