Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * Copyright (c) 2002-2005, Richard Heyes
4
 * All rights reserved.
5
 *
6
 * PHP version 4, 5
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 *
12
 *  o Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 *  o Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *  o The names of the authors may not be used to endorse or promote
18
 *    products derived from this software without specific prior written
19
 *    permission.
20
 *
21
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
 *
33
 * @category File
34
 * @package  File_SearchReplace
35
 * @author   Richard Heyes <richard@phpguru.org>
36
 * @license  http://www.opensource.org/licenses/bsd-license.php BSD
37
 * @version  CVS: $Id: SearchReplace.php 304758 2010-10-25 10:29:02Z clockwerx $
38
 * @link     http://pear.php.net/File_SearchReplace
39
 */
40
 
41
/**
42
 * Search and Replace Utility
43
 *
44
 * @category File
45
 * @package  File_SearchReplace
46
 * @author   Richard Heyes <richard@phpguru.org>
47
 * @license  http://www.opensource.org/licenses/bsd-license.php BSD
48
 * @link     http://pear.php.net/File_SearchReplace
49
 */
50
class File_SearchReplace
51
{
52
 
53
    // {{{ Properties (All private)
54
 
55
    var $find;
56
    var $replace;
57
    var $files;
58
    var $directories;
59
    var $include_subdir;
60
    var $ignore_lines;
61
    var $ignore_sep;
62
    var $occurences;
63
    var $search_function;
64
    var $php5;
65
    var $last_error;
66
 
67
    // }}}
68
    // {{{ Constructor
69
 
70
    /**
71
     * Sets up the object
72
     *
73
     * @param string $find           The string/regex to find.
74
     * @param string $replace        The string/regex to replace $find with.
75
     * @param array  $files          The file(s) to perform this operation on.
76
     * @param array  $directories    The directories to perform this operation on.
77
     * @param bool   $include_subdir If performing on directories, whether to
78
     *                               traverse subdirectories.
79
     * @param array  $ignore_lines   Ignore lines beginning with any of the strings
80
     *                               in this array. This
81
     *                               feature only works with the "normal" search.
82
     *
83
     * @access public
84
     */
85
    function File_SearchReplace($find, $replace, $files, $directories = '',
86
                                $include_subdir = true, $ignore_lines = array())
87
    {
88
 
89
        $this->setFind($find);
90
        $this->setReplace($replace);
91
        $this->setFiles($files);
92
 
93
        $this->setDirectories($directories);
94
        $this->setIncludeSubdir($include_subdir);
95
 
96
        $this->setIgnoreLines((array) $ignore_lines);
97
 
98
        $this->occurences      = 0;
99
        $this->search_function = 'search';
100
        $this->php5            = substr(PHP_VERSION, 0, 1) == 5;
101
        $this->last_error      = '';
102
 
103
    }
104
 
105
    // }}}
106
    // {{{ getNumOccurences()
107
 
108
    /**
109
     * Accessor to return the number of occurences found.
110
     *
111
     * @access public
112
     * @return int Number of occurences found.
113
     */
114
    function getNumOccurences()
115
    {
116
        return $this->occurences;
117
    }
118
 
119
    // }}}
120
    // {{{ getLastError()
121
 
122
    /**
123
     * Accessor for retrieving last error.
124
     *
125
     * @access public
126
     * @return string The last error that occurred, if any.
127
     */
128
    function getLastError()
129
    {
130
        return $this->last_error;
131
    }
132
 
133
    // }}}
134
    // {{{ setFind()
135
 
136
    /**
137
     * Accessor for setting find variable.
138
     *
139
     * @param mixed $find The string/regex to find, or array of strings
140
     *
141
     * @access public
142
     * @return void
143
     */
144
    function setFind($find)
145
    {
146
        $this->find = $find;
147
    }
148
 
149
    // }}}
150
    // {{{ setReplace()
151
 
152
    /**
153
     * Accessor for setting replace variable.
154
     *
155
     * @param mixed $replace The string/regex to replace the find
156
     * string/regex with, or array of strings
157
     *
158
     * @access public
159
     * @return void
160
     */
161
    function setReplace($replace)
162
    {
163
        $this->replace = $replace;
164
    }
165
 
166
    // }}}
167
    // {{{ setFiles()
168
 
169
    /**
170
     * Accessor for setting files variable.
171
     *
172
     * @param array $files The file(s) to perform this operation on.
173
     *
174
     * @access public
175
     * @return void
176
     */
177
    function setFiles($files)
178
    {
179
        $this->files = $files;
180
    }
181
 
182
    // }}}
183
    // {{{ setDirectories()
184
 
185
    /**
186
     * Accessor for setting directories variable.
187
     *
188
     * @param array $directories The directories to perform this operation on.
189
     *
190
     * @access public
191
     * @return void
192
     */
193
    function setDirectories($directories)
194
    {
195
        $this->directories = $directories;
196
    }
197
 
198
    // }}}
199
    // {{{ setIncludeSubdir
200
 
201
    /**
202
     * Accessor for setting include_subdir variable.
203
     *
204
     * @param bool $include_subdir Whether to traverse subdirectories or not.
205
     *
206
     * @access public
207
     * @return void
208
     */
209
    function setIncludeSubdir($include_subdir)
210
    {
211
        $this->include_subdir = $include_subdir;
212
    }
213
 
214
    // }}}
215
    // {{{ setIgnoreLines()
216
 
217
    /**
218
     * Accessor for setting ignore_lines variable.
219
     *
220
     * @param array $ignore_lines Ignore lines beginning with any of the
221
     *                            strings in this array. This
222
     *                            feature only works with the "normal" search.
223
     *
224
     * @access public
225
     * @return void
226
     */
227
    function setIgnoreLines($ignore_lines)
228
    {
229
        $this->ignore_lines = $ignore_lines;
230
    }
231
 
232
    // }}}
233
    // {{{ setSearchFunction()
234
 
235
    /**
236
     * Function to determine which search function is used.
237
     *
238
     * Can be any one of:
239
     *  normal - Default search. Goes line by line. Ignore lines feature
240
     *           only works with this type.
241
     *  quick  - Uses str_replace for straight replacement throughout
242
     *           file. Quickest of the lot.
243
     *  preg   - Uses preg_replace(), so any valid regex
244
     *
245
     * @param string $search_function The search function that should be used.
246
     *
247
     * @access public
248
     * @return void
249
     */
250
    function setSearchFunction($search_function)
251
    {
252
        switch($search_function) {
253
        case 'normal':
254
            $this->search_function = 'search';
255
            return true;
256
            break;
257
 
258
        case 'quick' :
259
            $this->search_function = 'quickSearch';
260
            return true;
261
            break;
262
 
263
        case 'preg'  :
264
            $this->search_function = 'pregSearch';
265
            return true;
266
            break;
267
 
268
        default      :
269
            $this->last_error = 'Invalid search function specified';
270
            return false;
271
            break;
272
        }
273
    }
274
 
275
    // }}}
276
    // {{{ search()
277
 
278
    /**
279
     * Default ("normal") search routine.
280
     *
281
     * @param string $filename The filename to search and replace upon.
282
     *
283
     * @access private
284
     * @return array Will return an array containing the new file contents
285
     *               and the number of occurences.
286
     *               Will return false if there are no occurences.
287
     */
288
    function search($filename)
289
    {
290
        $occurences = 0;
291
 
292
        $lines = file($filename);
293
 
294
        // just for the sake of catching occurences
295
        $local_find    = $this->getFind();
296
        $local_replace = $this->getReplace();
297
 
298
        if (empty($this->ignore_lines) && $this->php5) { // PHP5 acceleration
299
            $lines = str_replace($local_find, $local_replace,
300
                                      $lines, $occurences);
301
 
302
        } else { // str_replace() doesn't return number of occurences in PHP4
303
                 // so we need to count them manually and/or filter strings
304
            $ignore_lines_num = count($this->ignore_lines);
305
 
306
 
307
 
308
            foreach ($lines as $i => $line) {
309
 
310
                if ($ignore_lines_num > 0) {
311
                    for ($j = 0; $j < $ignore_lines_num; $j++) {
312
                        $text = substr($line, 0, strlen($this->ignore_lines[$j]));
313
                        if ($text == $this->ignore_lines[$j]) {
314
                            continue 2;
315
                        }
316
                    }
317
                }
318
 
319
                if ($this->php5) {
320
                    $lines[$i] = str_replace($local_find, $local_replace,
321
                                                  $line, $counted);
322
 
323
                    $occurences += $counted;
324
                } else {
325
                    foreach ($local_find as $fk => $ff) {
326
                        $occurences += substr_count($line, $ff);
327
                        if (!is_array($local_replace)) {
328
                            $fr = $local_replace;
329
                        } else {
330
                            $fr = "";
331
                            if (isset($local_replace[$fk])) {
332
                                $fr = $local_replace[$fk];
333
                            }
334
                        }
335
                        $lines[$i] = str_replace($ff, $fr, $line);
336
                    }
337
                }
338
            }
339
 
340
        }
341
 
342
        if ($occurences > 0) {
343
            return array($occurences, implode('', $lines));
344
        }
345
 
346
        return false;
347
    }
348
 
349
    // }}}
350
    // {{{ quickSearch()
351
 
352
    /**
353
     * Quick search routine.
354
     *
355
     * @param string $filename The filename to search and replace upon.
356
     *
357
     * @access private
358
     * @return array Will return an array containing the new file contents
359
     *               and the number of occurences.
360
     *               Will return false if there are no occurences.
361
     */
362
    function quickSearch($filename)
363
    {
364
 
365
        clearstatcache();
366
 
367
        $file = file_get_contents($filename);
368
 
369
        $local_find    = $this->getFind();
370
        $local_replace = $this->getReplace();
371
 
372
        $occurences = 0;
373
 
374
        // logic is the same as in str_replace function with one exception:
375
        //   if <search> is a string and <replacement> is an array - substitution
376
        //   is done from the first element of array. str_replace in this case
377
        //   usualy fails with notice and returns "ArrayArrayArray..." string
378
        // (this exclusive logic of SearchReplace will not work for php5, though,
379
        // because I haven't decided yet whether it is bug or feature)
380
 
381
        if ($this->php5) {
382
            $file = str_replace($local_find, $local_replace, $file, $counted);
383
 
384
            $occurences += $counted;
385
        } else {
386
            foreach ($local_find as $fk => $ff) {
387
                $occurences += substr_count($file, $ff);
388
                if (!is_array($local_replace)) {
389
                    $fr = $local_replace;
390
                } else {
391
                    $fr = isset($local_replace[$fk]) ? $local_replace[$fk] : "";
392
                }
393
                $file = str_replace($ff, $fr, $file);
394
            }
395
        }
396
 
397
        if ($occurences > 0) {
398
            return array($occurences, $file);
399
        }
400
 
401
        return false;
402
 
403
    }
404
 
405
    // }}}
406
    // {{{ pregSearch()
407
 
408
    /**
409
     * Preg search routine.
410
     *
411
     * @param string $filename The filename to search and replace upon.
412
     *
413
     * @access private
414
     * @return array Will return an array containing the new file contents
415
     *               and the number of occurences.
416
     *               Will return false if there are no occurences.
417
     */
418
    function pregSearch($filename)
419
    {
420
 
421
        clearstatcache();
422
 
423
        $file = file_get_contents($filename);
424
 
425
        $local_find    = $this->getFind();
426
        $local_replace = $this->getReplace();
427
 
428
        $occurences = 0;
429
 
430
        foreach ($local_find as $fk => $ff) {
431
            $occurences += preg_match_all($ff, $file, $matches);
432
            if (!is_array($local_replace)) {
433
                $fr = $local_replace;
434
            } else {
435
                $fr = isset($local_replace[$fk]) ? $local_replace[$fk] : "";
436
            }
437
            $file = preg_replace($ff, $fr, $file);
438
        }
439
 
440
        if ($occurences > 0) {
441
            return array($occurences, $file);
442
        }
443
 
444
        return false;
445
 
446
    }
447
 
448
    // }}}
449
    // {{{ writeout()
450
 
451
    /**
452
     * Function to writeout the file contents.
453
     *
454
     * @param string $filename The filename of the file to write.
455
     * @param string $contents The contents to write to the file.
456
     *
457
     * @access private
458
     * @return void
459
     */
460
    function writeout($filename, $contents)
461
    {
462
 
463
        if ($fp = @fopen($filename, 'w')) {
464
            flock($fp, 2);
465
            fwrite($fp, $contents);
466
            flock($fp, 3);
467
            fclose($fp);
468
        } else {
469
            $this->last_error = 'Could not open file: '.$filename;
470
        }
471
 
472
    }
473
 
474
    // }}}
475
    // {{{ doFiles()
476
 
477
    /**
478
     * Function called by doSearch() to go through any files that need searching.
479
     *
480
     * @param string $ser_func The search function to use.
481
     *
482
     * @access private
483
     * @return void
484
     */
485
    function doFiles($ser_func)
486
    {
487
        if (!is_array($this->files)) {
488
            $this->files = explode(',', $this->files);
489
        }
490
 
491
        foreach ($this->files as $file) {
492
            if ($file == '.' OR $file == '..') {
493
                continue;
494
            }
495
 
496
            if (is_dir($file)) {
497
                continue;
498
            }
499
 
500
            $newfile = $this->$ser_func($file);
501
            if (is_array($newfile)) {
502
                $this->writeout($file, $newfile[1]);
503
                $this->occurences += $newfile[0];
504
            }
505
        }
506
    }
507
 
508
    // }}}
509
    // {{{ doDirectories()
510
 
511
    /**
512
     * Function called by doSearch() to go through any directories that
513
     * need searching.
514
     *
515
     * @param string $ser_func The search function to use.
516
     *
517
     * @access private
518
     * @return void
519
     */
520
    function doDirectories($ser_func)
521
    {
522
        if (!is_array($this->directories)) {
523
            $this->directories = explode(',', $this->directories);
524
        }
525
 
526
        foreach ($this->directories as $directory) {
527
            $dh = opendir($directory);
528
            while ($file = readdir($dh)) {
529
                if ($file == '.' OR $file == '..') {
530
                    continue;
531
                }
532
 
533
                if (is_dir($directory.$file)) {
534
                    if ($this->include_subdir) {
535
                        $this->directories[] = $directory.$file.'/';
536
                        continue;
537
                    } else {
538
                        continue;
539
                    }
540
                }
541
 
542
                $newfile = $this->$ser_func($directory.$file);
543
                if (is_array($newfile) == true) {
544
                    $this->writeout($directory.$file, $newfile[1]);
545
                    $this->occurences += $newfile[0];
546
                }
547
            }
548
        }
549
    }
550
 
551
    // }}}
552
    // {{{ doSearch()
553
 
554
    /**
555
     * This starts the search/replace off. The behavior of this function will likely
556
     * to be changed in future versions to work in read only mode. If you want to do
557
     * actual replace with writing files - use doReplace method instead.
558
     *
559
     * @access public
560
     * @return void
561
     */
562
    function doSearch()
563
    {
564
        $this->doReplace();
565
    }
566
 
567
    // }}}
568
    // {{{ doReplace()
569
 
570
    /**
571
     * This starts the search/replace off. Call this to do the replace.
572
     * First do whatever files are specified, and/or if directories are specified,
573
     * do those too.
574
     *
575
     * @access public
576
     * @return void
577
     */
578
    function doReplace()
579
    {
580
        $this->occurences = 0;
581
        if (!empty($this->find)) {
582
            if (!empty($this->files)) {
583
                $this->doFiles($this->search_function);
584
            }
585
 
586
            if (!empty($this->directories)) {
587
                $this->doDirectories($this->search_function);
588
            }
589
        }
590
    }
591
 
592
    // }}}
593
 
594
    /**
595
     * Helper method to ensure we always have an array of things to find.
596
     *
597
     * @access public
598
     * @return array
599
     */
600
    function getFind()
601
    {
602
        return array_values((array) $this->find);
603
    }
604
 
605
    /**
606
     * Helper method to fetch replace
607
     *
608
     * @access public
609
     * @return mixed
610
     */
611
    function getReplace()
612
    {
613
        if (is_array($this->replace)) {
614
            return array_values($this->replace);
615
        }
616
 
617
        return $this->replace;
618
    }
619
}
620
?>