Failed Conditions
Push — psr2-pluginredux ( 89614c )
by Andreas
05:43 queued 03:04
created

lib/exe/css.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * DokuWiki StyleSheet creator
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Andreas Gohr <[email protected]>
7
 */
8
9
use dokuwiki\Cache\Cache;
0 ignored issues
show
This use statement conflicts with another class in this namespace, Cache.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
10
use dokuwiki\Extension\Event;
11
12
if(!defined('DOKU_INC')) define('DOKU_INC', __DIR__ .'/../../');
13
if(!defined('NOSESSION')) define('NOSESSION',true); // we do not use a session or authentication here (better caching)
14
if(!defined('DOKU_DISABLE_GZIP_OUTPUT')) define('DOKU_DISABLE_GZIP_OUTPUT',1); // we gzip ourself here
15
if(!defined('NL')) define('NL',"\n");
16
require_once(DOKU_INC.'inc/init.php');
17
18
// Main (don't run when UNIT test)
19
if(!defined('SIMPLE_TEST')){
20
    header('Content-Type: text/css; charset=utf-8');
21
    css_out();
22
}
23
24
25
// ---------------------- functions ------------------------------
26
27
/**
28
 * Output all needed Styles
29
 *
30
 * @author Andreas Gohr <[email protected]>
31
 */
32
function css_out(){
33
    global $conf;
34
    global $lang;
35
    global $config_cascade;
36
    global $INPUT;
37
38
    if ($INPUT->str('s') == 'feed') {
39
        $mediatypes = array('feed');
40
        $type = 'feed';
41
    } else {
42
        $mediatypes = array('screen', 'all', 'print', 'speech');
43
        $type = '';
44
    }
45
46
    // decide from where to get the template
47
    $tpl = trim(preg_replace('/[^\w-]+/','',$INPUT->str('t')));
48
    if(!$tpl) $tpl = $conf['template'];
49
50
    // load style.ini
51
    $styleUtil = new \dokuwiki\StyleUtils($tpl, $INPUT->bool('preview'));
52
    $styleini = $styleUtil->cssStyleini();
53
54
    // cache influencers
55
    $tplinc = tpl_incdir($tpl);
56
    $cache_files = getConfigFiles('main');
57
    $cache_files[] = $tplinc.'style.ini';
58
    $cache_files[] = DOKU_CONF."tpl/$tpl/style.ini";
59
    $cache_files[] = __FILE__;
60
    if($INPUT->bool('preview')) $cache_files[] = $conf['cachedir'].'/preview.ini';
61
62
    // Array of needed files and their web locations, the latter ones
63
    // are needed to fix relative paths in the stylesheets
64
    $media_files = array();
65
    foreach($mediatypes as $mediatype) {
66
        $files = array();
67
68
        // load core styles
69
        $files[DOKU_INC.'lib/styles/'.$mediatype.'.css'] = DOKU_BASE.'lib/styles/';
70
71
        // load jQuery-UI theme
72
        if ($mediatype == 'screen') {
73
            $files[DOKU_INC.'lib/scripts/jquery/jquery-ui-theme/smoothness.css'] =
74
                DOKU_BASE.'lib/scripts/jquery/jquery-ui-theme/';
75
        }
76
        // load plugin styles
77
        $files = array_merge($files, css_pluginstyles($mediatype));
78
        // load template styles
79
        if (isset($styleini['stylesheets'][$mediatype])) {
80
            $files = array_merge($files, $styleini['stylesheets'][$mediatype]);
81
        }
82
        // load user styles
83
        if(is_array($config_cascade['userstyle'][$mediatype])) {
84
            foreach($config_cascade['userstyle'][$mediatype] as $userstyle) {
85
                $files[$userstyle] = DOKU_BASE;
86
            }
87
        }
88
89
        // Let plugins decide to either put more styles here or to remove some
90
        $media_files[$mediatype] = css_filewrapper($mediatype, $files);
91
        $CSSEvt = new Event('CSS_STYLES_INCLUDED', $media_files[$mediatype]);
92
93
        // Make it preventable.
94
        if ( $CSSEvt->advise_before() ) {
95
            $cache_files = array_merge($cache_files, array_keys($media_files[$mediatype]['files']));
96
        } else {
97
            // unset if prevented. Nothing will be printed for this mediatype.
98
            unset($media_files[$mediatype]);
99
        }
100
101
        // finish event.
102
        $CSSEvt->advise_after();
103
    }
104
105
    // The generated script depends on some dynamic options
106
    $cache = new Cache(
107
        'styles' .
108
        $_SERVER['HTTP_HOST'] .
109
        $_SERVER['SERVER_PORT'] .
110
        $INPUT->bool('preview') .
111
        DOKU_BASE .
112
        $tpl .
113
        $type,
114
        '.css'
115
    );
116
    $cache->setEvent('CSS_CACHE_USE');
117
118
    // check cache age & handle conditional request
119
    // This may exit if a cache can be used
120
    $cache_ok = $cache->useCache(array('files' => $cache_files));
121
    http_cached($cache->cache, $cache_ok);
122
123
    // start output buffering
124
    ob_start();
125
126
    // Fire CSS_STYLES_INCLUDED for one last time to let the
127
    // plugins decide whether to include the DW default styles.
128
    // This can be done by preventing the Default.
129
    $media_files['DW_DEFAULT'] = css_filewrapper('DW_DEFAULT');
130
    Event::createAndTrigger('CSS_STYLES_INCLUDED', $media_files['DW_DEFAULT'], 'css_defaultstyles');
131
132
    // build the stylesheet
133
    foreach ($mediatypes as $mediatype) {
134
135
        // Check if there is a wrapper set for this type.
136
        if ( !isset($media_files[$mediatype]) ) {
137
            continue;
138
        }
139
140
        $cssData = $media_files[$mediatype];
141
142
        // Print the styles.
143
        print NL;
144
        if ( $cssData['encapsulate'] === true ) print $cssData['encapsulationPrefix'] . ' {';
145
        print '/* START '.$cssData['mediatype'].' styles */'.NL;
146
147
        // load files
148
        foreach($cssData['files'] as $file => $location){
149
            $display = str_replace(fullpath(DOKU_INC), '', fullpath($file));
150
            print "\n/* XXXXXXXXX $display XXXXXXXXX */\n";
151
            print css_loadfile($file, $location);
152
        }
153
154
        print NL;
155
        if ( $cssData['encapsulate'] === true ) print '} /* /@media ';
156
        else print '/*';
157
        print ' END '.$cssData['mediatype'].' styles */'.NL;
158
    }
159
160
    // end output buffering and get contents
161
    $css = ob_get_contents();
162
    ob_end_clean();
163
164
    // strip any source maps
165
    stripsourcemaps($css);
166
167
    // apply style replacements
168
    $css = css_applystyle($css, $styleini['replacements']);
169
170
    // parse less
171
    $css = css_parseless($css);
172
173
    // compress whitespace and comments
174
    if($conf['compress']){
175
        $css = css_compress($css);
176
    }
177
178
    // embed small images right into the stylesheet
179
    if($conf['cssdatauri']){
180
        $base = preg_quote(DOKU_BASE,'#');
181
        $css = preg_replace_callback('#(url\([ \'"]*)('.$base.')(.*?(?:\.(png|gif)))#i','css_datauri',$css);
182
    }
183
184
    http_cached_finish($cache->cache, $css);
185
}
186
187
/**
188
 * Uses phpless to parse LESS in our CSS
189
 *
190
 * most of this function is error handling to show a nice useful error when
191
 * LESS compilation fails
192
 *
193
 * @param string $css
194
 * @return string
195
 */
196
function css_parseless($css) {
197
    global $conf;
198
199
    $less = new lessc();
200
    $less->importDir = array(DOKU_INC);
201
    $less->setPreserveComments(!$conf['compress']);
202
203
    if (defined('DOKU_UNITTEST')){
204
        $less->importDir[] = TMP_DIR;
205
    }
206
207
    try {
208
        return $less->compile($css);
209
    } catch(Exception $e) {
210
        // get exception message
211
        $msg = str_replace(array("\n", "\r", "'"), array(), $e->getMessage());
212
213
        // try to use line number to find affected file
214
        if(preg_match('/line: (\d+)$/', $msg, $m)){
215
            $msg = substr($msg, 0, -1* strlen($m[0])); //remove useless linenumber
216
            $lno = $m[1];
217
218
            // walk upwards to last include
219
            $lines = explode("\n", $css);
220
            for($i=$lno-1; $i>=0; $i--){
221
                if(preg_match('/\/(\* XXXXXXXXX )(.*?)( XXXXXXXXX \*)\//', $lines[$i], $m)){
222
                    // we found it, add info to message
223
                    $msg .= ' in '.$m[2].' at line '.($lno-$i);
224
                    break;
225
                }
226
            }
227
        }
228
229
        // something went wrong
230
        $error = 'A fatal error occured during compilation of the CSS files. '.
231
            'If you recently installed a new plugin or template it '.
232
            'might be broken and you should try disabling it again. ['.$msg.']';
233
234
        echo ".dokuwiki:before {
235
            content: '$error';
236
            background-color: red;
237
            display: block;
238
            background-color: #fcc;
239
            border-color: #ebb;
240
            color: #000;
241
            padding: 0.5em;
242
        }";
243
244
        exit;
245
    }
246
}
247
248
/**
249
 * Does placeholder replacements in the style according to
250
 * the ones defined in a templates style.ini file
251
 *
252
 * This also adds the ini defined placeholders as less variables
253
 * (sans the surrounding __ and with a ini_ prefix)
254
 *
255
 * @author Andreas Gohr <[email protected]>
256
 *
257
 * @param string $css
258
 * @param array $replacements  array(placeholder => value)
259
 * @return string
260
 */
261
function css_applystyle($css, $replacements) {
262
    // we convert ini replacements to LESS variable names
263
    // and build a list of variable: value; pairs
264
    $less = '';
265
    foreach((array) $replacements as $key => $value) {
266
        $lkey = trim($key, '_');
267
        $lkey = '@ini_'.$lkey;
268
        $less .= "$lkey: $value;\n";
269
270
        $replacements[$key] = $lkey;
271
    }
272
273
    // we now replace all old ini replacements with LESS variables
274
    $css = strtr($css, $replacements);
275
276
    // now prepend the list of LESS variables as the very first thing
277
    $css = $less.$css;
278
    return $css;
279
}
280
281
/**
282
 * Wrapper for the files, content and mediatype for the event CSS_STYLES_INCLUDED
283
 *
284
 * @author Gerry Weißbach <[email protected]>
285
 *
286
 * @param string $mediatype type ofthe current media files/content set
287
 * @param array $files set of files that define the current mediatype
288
 * @return array
289
 */
290
function css_filewrapper($mediatype, $files=array()){
291
    return array(
292
            'files'                 => $files,
293
            'mediatype'             => $mediatype,
294
            'encapsulate'           => $mediatype != 'all',
295
            'encapsulationPrefix'   => '@media '.$mediatype
296
        );
297
}
298
299
/**
300
 * Prints the @media encapsulated default styles of DokuWiki
301
 *
302
 * @author Gerry Weißbach <[email protected]>
303
 *
304
 * This function is being called by a CSS_STYLES_INCLUDED event
305
 * The event can be distinguished by the mediatype which is:
306
 *   DW_DEFAULT
307
 */
308
function css_defaultstyles(){
309
    // print the default classes for interwiki links and file downloads
310
    print '@media screen {';
311
    css_interwiki();
312
    css_filetypes();
313
    print '}';
314
}
315
316
/**
317
 * Prints classes for interwikilinks
318
 *
319
 * Interwiki links have two classes: 'interwiki' and 'iw_$name>' where
320
 * $name is the identifier given in the config. All Interwiki links get
321
 * an default style with a default icon. If a special icon is available
322
 * for an interwiki URL it is set in it's own class. Both classes can be
323
 * overwritten in the template or userstyles.
324
 *
325
 * @author Andreas Gohr <[email protected]>
326
 */
327
function css_interwiki(){
328
329
    // default style
330
    echo 'a.interwiki {';
331
    echo ' background: transparent url('.DOKU_BASE.'lib/images/interwiki.png) 0px 1px no-repeat;';
332
    echo ' padding: 1px 0px 1px 16px;';
333
    echo '}';
334
335
    // additional styles when icon available
336
    $iwlinks = getInterwiki();
337
    foreach(array_keys($iwlinks) as $iw){
338
        $class = preg_replace('/[^_\-a-z0-9]+/i','_',$iw);
339
        if(file_exists(DOKU_INC.'lib/images/interwiki/'.$iw.'.png')){
340
            echo "a.iw_$class {";
341
            echo '  background-image: url('.DOKU_BASE.'lib/images/interwiki/'.$iw.'.png)';
342
            echo '}';
343
        }elseif(file_exists(DOKU_INC.'lib/images/interwiki/'.$iw.'.gif')){
344
            echo "a.iw_$class {";
345
            echo '  background-image: url('.DOKU_BASE.'lib/images/interwiki/'.$iw.'.gif)';
346
            echo '}';
347
        }
348
    }
349
}
350
351
/**
352
 * Prints classes for file download links
353
 *
354
 * @author Andreas Gohr <[email protected]>
355
 */
356
function css_filetypes(){
357
358
    // default style
359
    echo '.mediafile {';
360
    echo ' background: transparent url('.DOKU_BASE.'lib/images/fileicons/file.png) 0px 1px no-repeat;';
361
    echo ' padding-left: 18px;';
362
    echo ' padding-bottom: 1px;';
363
    echo '}';
364
365
    // additional styles when icon available
366
    // scan directory for all icons
367
    $exts = array();
368
    if($dh = opendir(DOKU_INC.'lib/images/fileicons')){
369
        while(false !== ($file = readdir($dh))){
370
            if(preg_match('/([_\-a-z0-9]+(?:\.[_\-a-z0-9]+)*?)\.(png|gif)/i',$file,$match)){
371
                $ext = strtolower($match[1]);
372
                $type = '.'.strtolower($match[2]);
373
                if($ext!='file' && (!isset($exts[$ext]) || $type=='.png')){
374
                    $exts[$ext] = $type;
375
                }
376
            }
377
        }
378
        closedir($dh);
379
    }
380
    foreach($exts as $ext=>$type){
381
        $class = preg_replace('/[^_\-a-z0-9]+/','_',$ext);
382
        echo ".mf_$class {";
383
        echo '  background-image: url('.DOKU_BASE.'lib/images/fileicons/'.$ext.$type.')';
384
        echo '}';
385
    }
386
}
387
388
/**
389
 * Loads a given file and fixes relative URLs with the
390
 * given location prefix
391
 *
392
 * @param string $file file system path
393
 * @param string $location
394
 * @return string
395
 */
396
function css_loadfile($file,$location=''){
397
    $css_file = new DokuCssFile($file);
398
    return $css_file->load($location);
399
}
400
401
/**
402
 *  Helper class to abstract loading of css/less files
403
 *
404
 *  @author Chris Smith <[email protected]>
405
 */
406
class DokuCssFile {
407
408
    protected $filepath;             // file system path to the CSS/Less file
409
    protected $location;             // base url location of the CSS/Less file
410
    protected $relative_path = null;
411
412
    public function __construct($file) {
413
        $this->filepath = $file;
414
    }
415
416
    /**
417
     * Load the contents of the css/less file and adjust any relative paths/urls (relative to this file) to be
418
     * relative to the dokuwiki root: the web root (DOKU_BASE) for most files; the file system root (DOKU_INC)
419
     * for less files.
420
     *
421
     * @param   string   $location   base url for this file
422
     * @return  string               the CSS/Less contents of the file
423
     */
424
    public function load($location='') {
425
        if (!file_exists($this->filepath)) return '';
426
427
        $css = io_readFile($this->filepath);
428
        if (!$location) return $css;
429
430
        $this->location = $location;
431
432
        $css = preg_replace_callback('#(url\( *)([\'"]?)(.*?)(\2)( *\))#',array($this,'replacements'),$css);
433
        $css = preg_replace_callback('#(@import\s+)([\'"])(.*?)(\2)#',array($this,'replacements'),$css);
434
435
        return $css;
436
    }
437
438
    /**
439
     * Get the relative file system path of this file, relative to dokuwiki's root folder, DOKU_INC
440
     *
441
     * @return string   relative file system path
442
     */
443
    protected function getRelativePath(){
444
445
        if (is_null($this->relative_path)) {
446
            $basedir = array(DOKU_INC);
447
448
            // during testing, files may be found relative to a second base dir, TMP_DIR
449
            if (defined('DOKU_UNITTEST')) {
450
                $basedir[] = realpath(TMP_DIR);
451
            }
452
453
            $basedir = array_map('preg_quote_cb', $basedir);
454
            $regex = '/^('.join('|',$basedir).')/';
455
            $this->relative_path = preg_replace($regex, '', dirname($this->filepath));
456
        }
457
458
        return $this->relative_path;
459
    }
460
461
    /**
462
     * preg_replace callback to adjust relative urls from relative to this file to relative
463
     * to the appropriate dokuwiki root location as described in the code
464
     *
465
     * @param  array    see http://php.net/preg_replace_callback
466
     * @return string   see http://php.net/preg_replace_callback
467
     */
468
    public function replacements($match) {
469
470
        if (preg_match('#^(/|data:|https?://)#', $match[3])) { // not a relative url? - no adjustment required
471
            return $match[0];
472
        } elseif (substr($match[3], -5) == '.less') { // a less file import? - requires a file system location
473
            if ($match[3]{0} != '/') {
474
                $match[3] = $this->getRelativePath() . '/' . $match[3];
475
            }
476
        } else { // everything else requires a url adjustment
477
            $match[3] = $this->location . $match[3];
478
        }
479
480
        return join('',array_slice($match,1));
481
    }
482
}
483
484
/**
485
 * Convert local image URLs to data URLs if the filesize is small
486
 *
487
 * Callback for preg_replace_callback
488
 *
489
 * @param array $match
490
 * @return string
491
 */
492
function css_datauri($match){
493
    global $conf;
494
495
    $pre   = unslash($match[1]);
496
    $base  = unslash($match[2]);
497
    $url   = unslash($match[3]);
498
    $ext   = unslash($match[4]);
499
500
    $local = DOKU_INC.$url;
501
    $size  = @filesize($local);
502
    if($size && $size < $conf['cssdatauri']){
503
        $data = base64_encode(file_get_contents($local));
504
    }
505
    if (!empty($data)){
506
        $url = 'data:image/'.$ext.';base64,'.$data;
507
    }else{
508
        $url = $base.$url;
509
    }
510
    return $pre.$url;
511
}
512
513
514
/**
515
 * Returns a list of possible Plugin Styles (no existance check here)
516
 *
517
 * @author Andreas Gohr <[email protected]>
518
 *
519
 * @param string $mediatype
520
 * @return array
521
 */
522
function css_pluginstyles($mediatype='screen'){
523
    $list = array();
524
    $plugins = plugin_list();
525
    foreach ($plugins as $p){
526
        $list[DOKU_PLUGIN."$p/$mediatype.css"]  = DOKU_BASE."lib/plugins/$p/";
527
        $list[DOKU_PLUGIN."$p/$mediatype.less"]  = DOKU_BASE."lib/plugins/$p/";
528
        // alternative for screen.css
529
        if ($mediatype=='screen') {
530
            $list[DOKU_PLUGIN."$p/style.css"]  = DOKU_BASE."lib/plugins/$p/";
531
            $list[DOKU_PLUGIN."$p/style.less"]  = DOKU_BASE."lib/plugins/$p/";
532
        }
533
    }
534
    return $list;
535
}
536
537
/**
538
 * Very simple CSS optimizer
539
 *
540
 * @author Andreas Gohr <[email protected]>
541
 *
542
 * @param string $css
543
 * @return string
544
 */
545
function css_compress($css){
546
    //strip comments through a callback
547
    $css = preg_replace_callback('#(/\*)(.*?)(\*/)#s','css_comment_cb',$css);
548
549
    //strip (incorrect but common) one line comments
550
    $css = preg_replace_callback('/^.*\/\/.*$/m','css_onelinecomment_cb',$css);
551
552
    // strip whitespaces
553
    $css = preg_replace('![\r\n\t ]+!',' ',$css);
554
    $css = preg_replace('/ ?([;,{}\/]) ?/','\\1',$css);
555
    $css = preg_replace('/ ?: /',':',$css);
556
557
    // number compression
558
    $css = preg_replace(
559
        '/([: ])0+(\.\d+?)0*((?:pt|pc|in|mm|cm|em|ex|px)\b|%)(?=[^\{]*[;\}])/',
560
        '$1$2$3',
561
        $css
562
    ); // "0.1em" to ".1em", "1.10em" to "1.1em"
563
    $css = preg_replace(
564
        '/([: ])\.(0)+((?:pt|pc|in|mm|cm|em|ex|px)\b|%)(?=[^\{]*[;\}])/',
565
        '$1$2',
566
        $css
567
    ); // ".0em" to "0"
568
    $css = preg_replace(
569
        '/([: ]0)0*(\.0*)?((?:pt|pc|in|mm|cm|em|ex|px)(?=[^\{]*[;\}])\b|%)/',
570
        '$1',
571
        $css
572
    ); // "0.0em" to "0"
573
    $css = preg_replace(
574
        '/([: ]\d+)(\.0*)((?:pt|pc|in|mm|cm|em|ex|px)(?=[^\{]*[;\}])\b|%)/',
575
        '$1$3',
576
        $css
577
    ); // "1.0em" to "1em"
578
    $css = preg_replace(
579
        '/([: ])0+(\d+|\d*\.\d+)((?:pt|pc|in|mm|cm|em|ex|px)(?=[^\{]*[;\}])\b|%)/',
580
        '$1$2$3',
581
        $css
582
    ); // "001em" to "1em"
583
584
    // shorten attributes (1em 1em 1em 1em -> 1em)
585
    $css = preg_replace(
586
        '/(?<![\w\-])((?:margin|padding|border|border-(?:width|radius)):)([\w\.]+)( \2)+(?=[;\}]| !)/',
587
        '$1$2',
588
        $css
589
    ); // "1em 1em 1em 1em" to "1em"
590
    $css = preg_replace(
591
        '/(?<![\w\-])((?:margin|padding|border|border-(?:width)):)([\w\.]+) ([\w\.]+) \2 \3(?=[;\}]| !)/',
592
        '$1$2 $3',
593
        $css
594
    ); // "1em 2em 1em 2em" to "1em 2em"
595
596
    // shorten colors
597
    $css = preg_replace(
598
        "/#([0-9a-fA-F]{1})\\1([0-9a-fA-F]{1})\\2([0-9a-fA-F]{1})\\3(?=[^\{]*[;\}])/",
599
        "#\\1\\2\\3",
600
        $css
601
    );
602
603
    return $css;
604
}
605
606
/**
607
 * Callback for css_compress()
608
 *
609
 * Keeps short comments (< 5 chars) to maintain typical browser hacks
610
 *
611
 * @author Andreas Gohr <[email protected]>
612
 *
613
 * @param array $matches
614
 * @return string
615
 */
616
function css_comment_cb($matches){
617
    if(strlen($matches[2]) > 4) return '';
618
    return $matches[0];
619
}
620
621
/**
622
 * Callback for css_compress()
623
 *
624
 * Strips one line comments but makes sure it will not destroy url() constructs with slashes
625
 *
626
 * @param array $matches
627
 * @return string
628
 */
629
function css_onelinecomment_cb($matches) {
630
    $line = $matches[0];
631
632
    $i = 0;
633
    $len = strlen($line);
634
635
    while ($i< $len){
636
        $nextcom = strpos($line, '//', $i);
637
        $nexturl = stripos($line, 'url(', $i);
638
639
        if($nextcom === false) {
640
            // no more comments, we're done
641
            $i = $len;
642
            break;
643
        }
644
645
        // keep any quoted string that starts before a comment
646
        $nextsqt = strpos($line, "'", $i);
647
        $nextdqt = strpos($line, '"', $i);
648
        if(min($nextsqt, $nextdqt) < $nextcom) {
649
            $skipto = false;
650
            if($nextsqt !== false && ($nextdqt === false || $nextsqt < $nextdqt)) {
651
                $skipto = strpos($line, "'", $nextsqt+1) +1;
652
            } else if ($nextdqt !== false) {
653
                $skipto = strpos($line, '"', $nextdqt+1) +1;
654
            }
655
656
            if($skipto !== false) {
657
                $i = $skipto;
658
                continue;
659
            }
660
        }
661
662
        if($nexturl === false || $nextcom < $nexturl) {
663
            // no url anymore, strip comment and be done
664
            $i = $nextcom;
665
            break;
666
        }
667
668
        // we have an upcoming url
669
        $i = strpos($line, ')', $nexturl);
670
    }
671
672
    return substr($line, 0, $i);
673
}
674
675
//Setup VIM: ex: et ts=4 :
676