Search & Replace - Version 3.2.0

Version Description

(2019-01-17) = * Added CSV format alternative for search/replace #82. * Improve code structure, preparation for more solid UnitTests. * Improve Modal Table UI. * Added Multiline searching #119. * Fix several issues to run always with php 5.6 to 7.2.

Download this release

Release Info

Developer Bueltge
Plugin Icon 128x128 Search & Replace
Version 3.2.0
Comparing to
See all releases

Code changes from version 3.1.2 to 3.2.0

Files changed (70) hide show
  1. license.txt → LICENSE +0 -0
  2. assets/css/inpsyde-search-replace.css +55 -56
  3. assets/css/inpsyde-search-replace.min.css +1 -1
  4. assets/js/inpsyde-search-replace.js +78 -49
  5. assets/js/inpsyde-search-replace.min.js +1 -1
  6. inc/Database/Exporter.php +391 -265
  7. inc/Database/Importer.php +23 -3
  8. inc/Database/Manager.php +22 -17
  9. inc/Database/Replace.php +230 -168
  10. inc/FileDownloader.php +233 -136
  11. inc/Load.php +0 -72
  12. inc/Page/AbstractPage.php +26 -16
  13. inc/Page/BackupDatabase.php +4 -4
  14. inc/Page/Credits.php +1 -1
  15. inc/Page/Manager.php +30 -16
  16. inc/Page/PageInterface.php +3 -3
  17. inc/Page/ReplaceDomain.php +20 -7
  18. inc/Page/SearchReplace.php +158 -123
  19. inc/Page/SqlImport.php +64 -38
  20. inc/Plugin.php +0 -48
  21. inc/Service/MaxExecutionTime.php +11 -11
  22. inc/requisite/src/Requisite/AutoLoaderInterface.php +0 -17
  23. inc/requisite/src/Requisite/Loader/DefaultConditionalFileLoader.php +0 -27
  24. inc/requisite/src/Requisite/Loader/DirectoryCacheFileLoader.php +0 -82
  25. inc/requisite/src/Requisite/Loader/FileLoaderInterface.php +0 -19
  26. inc/requisite/src/Requisite/Requisite.php +0 -72
  27. inc/requisite/src/Requisite/Rule/AutoLoadRuleInterface.php +0 -22
  28. inc/requisite/src/Requisite/Rule/NamespaceDirectoryMapper.php +0 -13
  29. inc/requisite/src/Requisite/Rule/Psr4.php +0 -82
  30. inc/requisite/src/Requisite/SPLAutoLoader.php +0 -57
  31. inc/templates/credits.php +121 -36
  32. inc/templates/{db_backup.php → db-backup.php} +10 -7
  33. inc/templates/replace-domain.php +93 -0
  34. inc/templates/replace_domain.php +0 -42
  35. inc/templates/search-replace.php +110 -0
  36. inc/templates/search_replace.php +0 -52
  37. inc/templates/sql-import.php +35 -0
  38. inc/templates/sql_import.php +0 -24
  39. inpsyde-search-replace.php +69 -29
  40. l10n/search-and-replace-de_DE.mo +0 -0
  41. l10n/search-and-replace-de_DE.po +0 -557
  42. l10n/search-and-replace-zh_CN.mo +0 -0
  43. l10n/search-and-replace-zh_CN.po +0 -489
  44. l10n/search-and-replace.pot +0 -455
  45. readme.txt +28 -8
  46. vendor/autoload.php +7 -0
  47. vendor/composer/ClassLoader.php +445 -0
  48. vendor/composer/LICENSE +21 -0
  49. vendor/composer/autoload_classmap.php +23 -0
  50. vendor/composer/autoload_namespaces.php +9 -0
  51. vendor/composer/autoload_psr4.php +10 -0
  52. vendor/composer/autoload_real.php +52 -0
  53. vendor/composer/autoload_static.php +49 -0
  54. vendor/composer/installed.json +2601 -0
  55. vendor/composer/semver/LICENSE +19 -0
  56. vendor/composer/semver/composer.json +58 -0
  57. vendor/composer/semver/src/Comparator.php +111 -0
  58. vendor/composer/semver/src/Constraint/AbstractConstraint.php +63 -0
  59. vendor/composer/semver/src/Constraint/Constraint.php +219 -0
  60. vendor/composer/semver/src/Constraint/ConstraintInterface.php +32 -0
  61. vendor/composer/semver/src/Constraint/EmptyConstraint.php +59 -0
  62. vendor/composer/semver/src/Constraint/MultiConstraint.php +120 -0
  63. vendor/composer/semver/src/Semver.php +127 -0
  64. vendor/composer/semver/src/VersionParser.php +548 -0
  65. vendor/composer/xdebug-handler/LICENSE +21 -0
  66. vendor/composer/xdebug-handler/composer.json +40 -0
  67. vendor/composer/xdebug-handler/src/PhpConfig.php +73 -0
  68. vendor/composer/xdebug-handler/src/Process.php +160 -0
  69. vendor/composer/xdebug-handler/src/Status.php +163 -0
  70. vendor/composer/xdebug-handler/src/XdebugHandler.php +540 -0
license.txt → LICENSE RENAMED
File without changes
assets/css/inpsyde-search-replace.css CHANGED
@@ -1,98 +1,96 @@
1
- input[name="search"], input[name="replace"], #select_tables {
2
- width: 100%;
3
  max-width: 400px;
4
  }
5
 
6
  .search-replace-search-value {
7
- color: #fff;
8
- background-color: red;
9
  }
10
 
11
  .search-replace-replace-value {
12
- color: #fff;
13
- background-color: green;
14
  }
15
 
16
  .disabled, .form-table .disabled th {
17
  color: grey;
18
  }
19
 
20
-
21
  /*width of file upload input on import page */
22
  #file_to_upload {
23
  width: 100%;
24
  }
25
 
26
  /*styles for "detailed view" modal */
27
- .search-replace-modal-background {
28
- position: fixed;
29
- top: 0;
30
- left: 0;
31
- right: 0;
32
- bottom: 0;
33
- min-height: 360px;
34
- background: #000;
35
- opacity: .7;
36
- z-index: 159900;
37
- }
38
-
39
  .search-replace-modal {
40
- position: fixed;
41
- top: 30px;
42
- left: 30px;
43
- right: 30px;
44
- bottom: 30px;
45
- z-index: 160000;
46
  background-color: #fff;
 
 
 
 
 
47
  overflow: hidden;
48
  }
49
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  .search-replace-modal-header .notice-dismiss {
51
- padding: 5px 1px;
 
 
52
  }
53
 
54
  .search-replace-changes-modal-content {
55
- position: absolute;
56
  overflow: auto;
57
- height: 100%;
58
- top: 50px;
 
59
  }
60
 
61
  .search-replace-modal-table {
62
- width: 100%;
63
- table-layout: fixed;
64
  margin-bottom: 30px;
65
  }
66
 
67
  .search-replace-modal-table * {
68
- word-wrap: break-word;
69
  vertical-align: baseline;
70
  }
71
 
72
- .search-replace-modal-table col:nth-child(1) {
73
  width: 50px;
74
  }
75
 
76
- .search-replace-modal-table col:nth-child(2) {
77
- width: 35px;
 
78
  }
79
 
80
- .search-replace-modal-table col:nth-child(3) {
81
- width: 100px;
82
  }
83
 
84
- .search-replace-modal-table col:nth-child(4) {
85
  width: 150px;
86
  }
87
 
88
- .search-replace-modal-table col:nth-child(5) {
89
- width: 100px;
90
- }
91
-
92
- .search-replace-modal-table col:nth-child(7) {
93
- width: 100px;
94
- }
95
-
96
  .search-replace-modal-table tr:nth-child(odd) {
97
  background-color: #eee;
98
  }
@@ -103,21 +101,22 @@ input[name="search"], input[name="replace"], #select_tables {
103
  }
104
 
105
  .search-replace-modal-close-button {
106
- float: right;
107
  background: #fff;
108
- border: none;
109
- padding: 15px;
 
110
  }
111
 
112
  .search-replace-modal-close-button:before {
113
- content: "\f158";
114
- font: 400 20px/1 dashicons;
115
- vertical-align: middle;
116
- -webkit-font-smoothing: antialiased;
117
  -moz-osx-font-smoothing: grayscale;
118
- color: #666;
119
  }
120
 
121
- #insr_submit{
122
  margin-bottom: 10px;
123
- }
1
+ textarea[name="search"], input[name="search"], input[name="replace"], #select_tables {
2
+ width: 100%;
3
  max-width: 400px;
4
  }
5
 
6
  .search-replace-search-value {
7
+ background-color: rgba(239, 49, 54, 0.4);
 
8
  }
9
 
10
  .search-replace-replace-value {
11
+ background-color: rgba(158, 219, 126, 0.6);
 
12
  }
13
 
14
  .disabled, .form-table .disabled th {
15
  color: grey;
16
  }
17
 
 
18
  /*width of file upload input on import page */
19
  #file_to_upload {
20
  width: 100%;
21
  }
22
 
23
  /*styles for "detailed view" modal */
 
 
 
 
 
 
 
 
 
 
 
 
24
  .search-replace-modal {
25
+ position: fixed;
26
+ top: 30px;
27
+ left: 30px;
28
+ right: 30px;
29
+ bottom: 30px;
30
+ z-index: 160000;
31
  background-color: #fff;
32
+ overflow: hidden;
33
+ }
34
+
35
+ /* set to body */
36
+ .search-replace-modal-open {
37
  overflow: hidden;
38
  }
39
 
40
+ .search-replace-modal-background {
41
+ position: fixed;
42
+ top: 0;
43
+ left: 0;
44
+ right: 0;
45
+ bottom: 0;
46
+ min-height: 360px;
47
+ background: #000;
48
+ opacity: .7;
49
+ z-index: 159900;
50
+ }
51
+
52
  .search-replace-modal-header .notice-dismiss {
53
+ /* because of the closer floating */
54
+ overflow: hidden;
55
+ padding: 5px 1px;
56
  }
57
 
58
  .search-replace-changes-modal-content {
59
+ height: calc(100% - 50px);
60
  overflow: auto;
61
+ padding: 0 1.63em;
62
+ position: absolute;
63
+ top: 50px;
64
  }
65
 
66
  .search-replace-modal-table {
67
+ width: 100%;
68
+ table-layout: fixed;
69
  margin-bottom: 30px;
70
  }
71
 
72
  .search-replace-modal-table * {
73
+ word-wrap: break-word;
74
  vertical-align: baseline;
75
  }
76
 
77
+ .search-replace-modal-table .search-replace-narrow {
78
  width: 50px;
79
  }
80
 
81
+ .search-replace-modal-table th,
82
+ .search-replace-modal-table td {
83
+ padding: .43em;
84
  }
85
 
86
+ .search-replace-modal-table .search-replace-row {
87
+ text-align: center;
88
  }
89
 
90
+ .search-replace-modal-table .search-replace-column {
91
  width: 150px;
92
  }
93
 
 
 
 
 
 
 
 
 
94
  .search-replace-modal-table tr:nth-child(odd) {
95
  background-color: #eee;
96
  }
101
  }
102
 
103
  .search-replace-modal-close-button {
104
+ cursor: pointer;
105
  background: #fff;
106
+ border: none;
107
+ float: right;
108
+ padding: 15px;
109
  }
110
 
111
  .search-replace-modal-close-button:before {
112
+ content: "\f158";
113
+ font: 400 20px/1 dashicons;
114
+ vertical-align: middle;
115
+ -webkit-font-smoothing: antialiased;
116
  -moz-osx-font-smoothing: grayscale;
117
+ color: #666;
118
  }
119
 
120
+ #insr_submit {
121
  margin-bottom: 10px;
122
+ }
assets/css/inpsyde-search-replace.min.css CHANGED
@@ -1 +1 @@
1
- #select_tables,input[name=search],input[name=replace]{width:100%;max-width:400px}.search-replace-search-value{color:#fff;background-color:red}.search-replace-replace-value{color:#fff;background-color:green}.disabled,.form-table .disabled th{color:grey}#file_to_upload{width:100%}.search-replace-modal-background{position:fixed;top:0;left:0;right:0;bottom:0;min-height:360px;background:#000;opacity:.7;z-index:159900}.search-replace-modal{position:fixed;top:30px;left:30px;right:30px;bottom:30px;z-index:160000;background-color:#fff;overflow:hidden}.search-replace-modal-header .notice-dismiss{padding:5px 1px}.search-replace-changes-modal-content{position:absolute;overflow:auto;height:100%;top:50px}.search-replace-modal-table{width:100%;table-layout:fixed;margin-bottom:30px}.search-replace-modal-table *{word-wrap:break-word;vertical-align:baseline}.search-replace-modal-table col:nth-child(1){width:50px}.search-replace-modal-table col:nth-child(2){width:35px}.search-replace-modal-table col:nth-child(3){width:100px}.search-replace-modal-table col:nth-child(4){width:150px}.search-replace-modal-table col:nth-child(5),.search-replace-modal-table col:nth-child(7){width:100px}.search-replace-modal-table tr:nth-child(odd){background-color:#eee}.search-replace-modal-table-headline{margin-left:10px;font-weight:300}.search-replace-modal-close-button{float:right;background:#fff;border:none;padding:15px}.search-replace-modal-close-button:before{content:"\f158";font:400 20px/1 dashicons;vertical-align:middle;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#666}#insr_submit{margin-bottom:10px}
1
+ #select_tables,input[name=search],input[name=replace],textarea[name=search]{width:100%;max-width:400px}.search-replace-search-value{background-color:rgba(239,49,54,.4)}.search-replace-replace-value{background-color:rgba(158,219,126,.6)}.disabled,.form-table .disabled th{color:grey}#file_to_upload{width:100%}.search-replace-modal{position:fixed;top:30px;left:30px;right:30px;bottom:30px;z-index:160000;background-color:#fff;overflow:hidden}.search-replace-modal-open{overflow:hidden}.search-replace-modal-background{position:fixed;top:0;left:0;right:0;bottom:0;min-height:360px;background:#000;opacity:.7;z-index:159900}.search-replace-modal-header .notice-dismiss{overflow:hidden;padding:5px 1px}.search-replace-changes-modal-content{height:calc(100% - 50px);overflow:auto;padding:0 1.63em;position:absolute;top:50px}.search-replace-modal-table{width:100%;table-layout:fixed;margin-bottom:30px}.search-replace-modal-table *{word-wrap:break-word;vertical-align:baseline}.search-replace-modal-table .search-replace-narrow{width:50px}.search-replace-modal-table td,.search-replace-modal-table th{padding:.43em}.search-replace-modal-table .search-replace-row{text-align:center}.search-replace-modal-table .search-replace-column{width:150px}.search-replace-modal-table tr:nth-child(odd){background-color:#eee}.search-replace-modal-table-headline{margin-left:10px;font-weight:300}.search-replace-modal-close-button{cursor:pointer;background:#fff;border:none;float:right;padding:15px}.search-replace-modal-close-button:before{content:"\f158";font:400 20px/1 dashicons;vertical-align:middle;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#666}#insr_submit{margin-bottom:10px}
assets/js/inpsyde-search-replace.js CHANGED
@@ -3,70 +3,99 @@
3
  */
4
  "use strict";
5
 
6
- jQuery( document ).ready( function() {
7
 
8
- (
9
- function( $ ) {
10
- //search-replace-tab: greys out export/save to db option when dry run is selected
11
- $( '#dry_run' ).click( toggle_disabled_attribute );
12
 
13
- //replace-domain-tab: greys out replace db_prefix option
14
- $ ('#change_db_prefix' ).click (toggle_disabled_attribute);
15
 
16
- function toggle_disabled_attribute() {
 
17
 
18
- var grayed_out_areas = $( '.maybe_disabled' );
19
- grayed_out_areas.toggleClass( 'disabled' );
20
- var inputs = (
21
- $( '.maybe_disabled input' )
22
- );
23
 
24
- if ( inputs.attr( 'disabled' ) ) {
25
- inputs.removeAttr( 'disabled' );
26
- }
 
 
 
27
 
28
- else {
29
- inputs.attr( 'disabled', true );
30
- }
31
 
 
 
 
 
32
  }
 
33
 
34
- //click on checkbox selects all tables in table select on search and replace tab
35
- var table_select_checkbox = $( '#select_all_tables' );
36
- table_select_checkbox.change( toggle_select_all_tables );
37
 
38
- function toggle_select_all_tables( e ) {
39
- console.log( e );
40
- if ( table_select_checkbox.is( ':checked' ) ) {
41
- $( '#select_tables option' ).attr( 'selected', true );
42
- }
43
- else {
44
- $( '#select_tables option' ).attr( 'selected', false );
45
- }
46
 
47
- }
 
 
48
 
49
- //event listener for changes modal
50
- $( '#changes-modal-button' ).click( show_changes_modal );
51
- $( '#changes-modal-close' ).click( hide_changes_modal );
52
 
53
- function show_changes_modal() {
54
- $( '#changes-modal, #changes-modal-background' ).show();
55
- //add listener for close with "esc" - key
56
- $( document ).bind( 'keydown', keydown_event_handler );
57
- }
58
 
59
- function hide_changes_modal() {
60
- $( '#changes-modal, #changes-modal-background' ).hide();
61
- }
62
 
63
- function keydown_event_handler( e ) {
64
- if ( e.keyCode === 27 ) {
65
- hide_changes_modal();
66
- $( document ).unbind( 'keydown', keydown_event_handler )
67
- }
68
  }
69
  }
70
- )( jQuery );
71
 
72
- } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  */
4
  "use strict";
5
 
6
+ ;(function ( $ ) {
7
 
8
+ window.addEventListener( 'load', function () {
 
 
 
9
 
10
+ function toggle_disabled_attribute() {
 
11
 
12
+ var grayed_out_areas = $( '.maybe_disabled' );
13
+ grayed_out_areas.toggleClass( 'disabled' );
14
 
15
+ var inputs = $( '.maybe_disabled input' );
 
 
 
 
16
 
17
+ if ( inputs.attr( 'disabled' ) ) {
18
+ inputs.removeAttr( 'disabled' );
19
+ } else {
20
+ inputs.attr( 'disabled', true );
21
+ }
22
+ }
23
 
24
+ function toggle_select_all_tables( e ) {
 
 
25
 
26
+ if ( table_select_checkbox.is( ':checked' ) ) {
27
+ $( '#select_tables option' ).attr( 'selected', true );
28
+ } else {
29
+ $( '#select_tables option' ).attr( 'selected', false );
30
  }
31
+ }
32
 
33
+ function show_changes_modal() {
 
 
34
 
35
+ $( '#changes-modal, #changes-modal-background' ).show();
36
+ $(document.body).toggleClass('search-replace-modal-open');
 
 
 
 
 
 
37
 
38
+ //add listener for close with "esc" - key
39
+ $( document ).bind( 'keydown', keydown_event_handler );
40
+ }
41
 
42
+ function hide_changes_modal() {
 
 
43
 
44
+ $( '#changes-modal, #changes-modal-background' ).hide();
45
+ $(document.body).toggleClass('search-replace-modal-open');
46
+ }
 
 
47
 
48
+ function keydown_event_handler( e ) {
 
 
49
 
50
+ // ESC
51
+ if ( e.keyCode === 27 ) {
52
+ hide_changes_modal();
53
+ $( document ).unbind( 'keydown', keydown_event_handler )
 
54
  }
55
  }
 
56
 
57
+ //search-replace-tab: greys out export/save to db option when dry run is selected
58
+ $( '#dry_run' ).click( toggle_disabled_attribute );
59
+
60
+ //replace-domain-tab: greys out replace db_prefix option
61
+ $( '#change_db_prefix' ).click( toggle_disabled_attribute );
62
+
63
+ //click on checkbox selects all tables in table select on search and replace tab
64
+ var table_select_checkbox = $( '#select_all_tables' );
65
+ table_select_checkbox.change( toggle_select_all_tables );
66
+
67
+ // GZ compression only available for exports
68
+ $( '#radio2' ).click( function () {
69
+ $( '#compress' ).attr( 'checked', false );
70
+ $( ' #compress' ).attr( 'disabled', true );
71
+ } );
72
+
73
+ $( '#radio1' ).click( function () {
74
+ $( ' #compress' ).removeAttr( 'disabled' );
75
+ } );
76
+
77
+ //event listener for changes modal
78
+ $( '#changes-modal-button' ).click( show_changes_modal );
79
+ $( '#changes-modal-close' ).click( hide_changes_modal );
80
+
81
+ // Trying to search for site URL will give warning
82
+ // But only if it is not an email address
83
+ $( '#search-submit' ).click( function () {
84
+ if ( $( '#radio2' ).is( ':checked' )
85
+ && $( '#search' ).val().indexOf( insr_data_obj.site_url ) != - 1
86
+ && $( '#search' ).val().indexOf( '@' ) == - 1 ) {
87
+ return confirm( insr_data_obj.search_matches_site_url );
88
+ }
89
+ return true;
90
+ } );
91
+
92
+ // Auto resize textarea search field if the content is multilines
93
+ var search_textarea = $( 'textarea#search' );
94
+ search_textarea.on('input change drop keydown cut paste', function() {
95
+ search_textarea.height('auto');
96
+ search_textarea.height(search_textarea.prop('scrollHeight'));
97
+ }).trigger('input');
98
+
99
+ } );
100
+
101
+ }( window.jQuery ));
assets/js/inpsyde-search-replace.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";jQuery(document).ready(function(){!function(a){function b(){var b=a(".maybe_disabled");b.toggleClass("disabled");var c=a(".maybe_disabled input");c.attr("disabled")?c.removeAttr("disabled"):c.attr("disabled",!0)}function c(b){console.log(b),g.is(":checked")?a("#select_tables option").attr("selected",!0):a("#select_tables option").attr("selected",!1)}function d(){a("#changes-modal, #changes-modal-background").show(),a(document).bind("keydown",f)}function e(){a("#changes-modal, #changes-modal-background").hide()}function f(b){27===b.keyCode&&(e(),a(document).unbind("keydown",f))}a("#dry_run").click(b),a("#change_db_prefix").click(b);var g=a("#select_all_tables");g.change(c),a("#changes-modal-button").click(d),a("#changes-modal-close").click(e)}(jQuery)});
1
+ "use strict";!function(a){window.addEventListener("load",function(){function b(){var b=a(".maybe_disabled");b.toggleClass("disabled");var c=a(".maybe_disabled input");c.attr("disabled")?c.removeAttr("disabled"):c.attr("disabled",!0)}function c(b){g.is(":checked")?a("#select_tables option").attr("selected",!0):a("#select_tables option").attr("selected",!1)}function d(){a("#changes-modal, #changes-modal-background").show(),a(document.body).toggleClass("search-replace-modal-open"),a(document).bind("keydown",f)}function e(){a("#changes-modal, #changes-modal-background").hide(),a(document.body).toggleClass("search-replace-modal-open")}function f(b){27===b.keyCode&&(e(),a(document).unbind("keydown",f))}a("#dry_run").click(b),a("#change_db_prefix").click(b);var g=a("#select_all_tables");g.change(c),a("#radio2").click(function(){a("#compress").attr("checked",!1),a(" #compress").attr("disabled",!0)}),a("#radio1").click(function(){a(" #compress").removeAttr("disabled")}),a("#changes-modal-button").click(d),a("#changes-modal-close").click(e),a("#search-submit").click(function(){return!a("#radio2").is(":checked")||a("#search").val().indexOf(insr_data_obj.site_url)==-1||a("#search").val().indexOf("@")!=-1||confirm(insr_data_obj.search_matches_site_url)});var h=a("textarea#search");h.on("input change drop keydown cut paste",function(){h.height("auto"),h.height(h.prop("scrollHeight"))}).trigger("input")})}(window.jQuery);
inc/Database/Exporter.php CHANGED
@@ -1,63 +1,71 @@
1
- <?php
2
 
3
  namespace Inpsyde\SearchReplace\Database;
4
 
5
  /**
6
  * Class Exporter
7
  *
 
8
  * @package Inpsyde\SearchReplace\Database
9
  */
10
  class Exporter {
11
 
12
  /**
13
- * @Stores all error messages in a WP_Error Object
14
  */
15
- protected $errors;
16
 
17
  /**
18
  * @string The Path to the Backup Directory
19
  */
20
- protected $backup_dir;
21
 
22
  /**
23
  * @var Replace
24
  */
25
- protected $replace;
26
 
27
  /**
28
  * @var Manager
29
  */
30
- protected $dbm;
31
 
32
  /**
33
  * Count of rows to be replaced at a time
34
  *
35
  * @var int
36
  */
37
-
38
- protected $page_size = 100;
39
 
40
  /**
41
- * @String stores the filename of the backup file
42
  */
43
- protected $backup_filename;
44
 
45
  /**
46
  * Store file path.
47
  *
48
  * @var $fp
49
  */
50
- protected $fp;
 
 
 
 
 
 
 
51
 
52
  /**
53
  * Exporter constructor.
54
  *
55
  * @param Replace $replace
56
  * @param Manager $dbm
 
57
  */
58
- public function __construct( Replace $replace, Manager $dbm ) {
59
 
60
- $this->errors = new \WP_Error();
61
  $this->backup_dir = get_temp_dir();
62
  $this->replace = $replace;
63
  $this->dbm = $dbm;
@@ -68,124 +76,213 @@ class Exporter {
68
  *
69
  * @param string $search
70
  * @param string $replace
71
- * @param array $tables The array of table names that should be exported.
72
- * @param bool $domain_replace If set, exporter will change the domain name without leading http:// in table
73
  * wp_blogs if we are on a multisite
74
- * @param $new_table_prefix
 
75
  *
76
  * @return array $report $report [ 'filename'] : Name of Backup file,
77
  * $report[ 'errors'] : WP_Error_object,
78
  * $report ['changes'] : Array with replacements in tables
 
79
  */
80
- public function db_backup( $search = '', $replace = '', $tables = array(), $domain_replace = FALSE, $new_table_prefix = '' ) {
 
 
 
 
 
 
 
81
 
82
  if ( count( $tables ) < 1 ) {
83
  $tables = $this->dbm->get_tables();
84
  }
85
 
86
- $report = array(
87
- 'errors' => NULL,
88
- 'changes' => array(),
89
  'tables' => '0',
90
  'changes_count' => '0',
91
  'filename' => '',
92
- );
93
 
94
  $table_prefix = $this->dbm->get_base_prefix();
95
 
96
  // wp_blogs needs special treatment in multisite domain replace, we need to check later if we are working on it.
97
  $wp_blogs_table = $table_prefix . 'blogs';
98
 
99
- $this->backup_filename = $new_table_prefix === '' ? DB_NAME . "_$table_prefix.sql"
100
- : DB_NAME . "_$new_table_prefix.sql";
 
101
 
102
- if ( is_writable( $this->backup_dir ) ) {
103
- $this->fp = $this->open( $this->backup_dir . $this->backup_filename );
104
- if ( ! $this->fp ) {
105
- $this->errors->add(
106
- 8, esc_attr__( 'Could not open the backup file for writing!', 'search-and-replace' )
107
- );
108
-
109
- return $report;
110
- }
111
- } else {
112
  $this->errors->add( 9, esc_attr__( 'The backup directory is not writable!', 'search-and-replace' ) );
113
 
114
  return $report;
115
  }
116
 
117
- //Begin new backup of MySql
118
- //get charset. if not set assume utf8
119
- $charset = ( defined( 'DB_CHARSET' ) ? DB_CHARSET : 'utf8' );
 
 
 
 
 
 
 
 
 
 
 
120
  $this->stow( '# ' . esc_attr__( 'WordPress MySQL database backup', 'search-and-replace' ) . "\n" );
121
  $this->stow( "#\n" );
122
  $this->stow( '# ' . sprintf( __( 'Generated: %s', 'search-and-replace' ), date( 'l j. F Y H:i T' ) ) . "\n" );
123
  $this->stow( '# ' . sprintf( __( 'Hostname: %s', 'search-and-replace' ), DB_HOST ) . "\n" );
124
  $this->stow( '# ' . sprintf( __( 'Database: %s', 'search-and-replace' ), $this->backquote( DB_NAME ) ) . "\n" );
 
125
  if ( '' !== $new_table_prefix ) {
126
  $this->stow(
127
  '# ' . sprintf(
128
- __( 'Changed table prefix: From %s to %s ', 'search-and-replace' ),
 
129
  $table_prefix,
130
  $new_table_prefix
131
  )
132
  . "\n"
133
  );
134
  }
135
- $this->stow( "# --------------------------------------------------------\n" );
136
 
 
137
  $this->stow( "/*!40101 SET NAMES $charset */;\n" );
138
  $this->stow( "# --------------------------------------------------------\n" );
 
139
  foreach ( $tables as $table ) {
140
 
141
- //count tables
142
- $report [ 'tables' ] ++;
143
 
144
  /**
145
  * Check if we are replacing the domain in a multisite.
146
  * If so, we replace in wp_blogs the stripped url without http(s), because the domains
147
  * are stored without http://
148
  */
149
- if ( $domain_replace && is_multisite() && $table === $wp_blogs_table ) {
150
-
151
  $stripped_url_search = substr( $search, strpos( $search, '/' ) + 2 );
152
  $stripped_url_replace = substr( $replace, strpos( $replace, '/' ) + 2 );
153
- $table_report = $this->backup_table(
 
 
154
  $stripped_url_search,
155
  $stripped_url_replace,
156
  $table,
157
  $new_table_prefix
158
  );
159
-
160
  } else {
161
- $table_report = $this->backup_table( $search, $replace, $table, $new_table_prefix );
 
162
  }
163
- //log changes if any
164
 
165
- if ( 0 !== $table_report[ 'change' ] ) {
166
- $report[ 'changes' ][ $table ] = $table_report;
 
167
 
168
- $report [ 'changes_count' ] += $table_report[ 'change' ];
169
  }
170
  }
171
 
172
  $this->close( $this->fp );
173
- //return errors if any
 
174
  if ( count( $this->errors->get_error_codes() ) ) {
175
- $report[ 'errors' ] = $this->errors;
176
  }
177
- $report [ 'filename' ] = $this->backup_filename;
 
178
 
179
  return $report;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  }
182
 
183
  /**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  * Taken partially from phpMyAdmin and partially from
185
  * Alain Wolf, Zurich - Switzerland
 
186
  * Website: http://restkultur.ch/personal/wolf/scripts/db_backup/
187
- * Modified by Scott Merrill (http://www.skippy.net/)
188
- * to use the WordPress $wpdb object
189
  *
190
  * @param string $search
191
  * @param string $replace
@@ -193,29 +290,41 @@ class Exporter {
193
  * @param string $new_table_prefix
194
  *
195
  * @return array $table_report Reports the changes made to the db.
 
196
  */
 
197
 
198
- public function backup_table( $search = '', $replace = '', $table, $new_table_prefix = '' ) {
199
-
200
- $table_report = array(
201
  'table_name' => $table,
202
  'rows' => 0,
203
  'change' => 0,
204
- 'changes' => [ ],
205
- );
206
- //do we need to replace the prefix?
 
 
 
 
 
 
 
 
 
 
207
  $table_prefix = $this->dbm->get_base_prefix();
208
  $new_table = $table;
 
209
  if ( '' !== $new_table_prefix ) {
210
  $new_table = $this->get_new_table_name( $table, $new_table_prefix );
211
-
212
  }
213
 
214
  // Create the SQL statements
215
  $this->stow( '# --------------------------------------------------------' . "\n" );
216
  $this->stow( '# ' . sprintf( __( 'Table: %s', 'search-and-replace' ), $this->backquote( $new_table ) ) . "\n" );
217
 
 
218
  $table_structure = $this->dbm->get_table_structure( $table );
 
219
  if ( ! $table_structure ) {
220
  $this->errors->add( 1, __( 'Error getting table details', 'search-and-replace' ) . ": $table" );
221
 
@@ -249,20 +358,24 @@ class Exporter {
249
 
250
  /** @var array $create_table */
251
  $create_table = $this->dbm->get_create_table_statement( $table );
252
- if ( FALSE === $create_table ) {
 
 
253
  $err_msg = sprintf( __( 'Error with SHOW CREATE TABLE for %s.', 'search-and-replace' ), $table );
254
  $this->errors->add( 2, $err_msg );
255
  $this->stow( "#\n# $err_msg\n#\n" );
256
  }
257
- //replace prefix if necessary
258
- if ( '' !== $new_table_prefix ) {
259
-
260
- $create_table[ 0 ][ 1 ] = str_replace( $table, $new_table, $create_table[ 0 ][ 1 ] );
261
 
 
 
 
 
262
  }
263
- $this->stow( $create_table[ 0 ][ 1 ] . ' ;' );
264
 
265
- if ( FALSE === $table_structure ) {
 
 
 
266
  $err_msg = sprintf( __( 'Error getting table structure of %s', 'search-and-replace' ), $table );
267
  $this->errors->add( 3, $err_msg );
268
  $this->stow( "#\n# $err_msg\n#\n" );
@@ -272,35 +385,54 @@ class Exporter {
272
  $this->stow( "\n\n" );
273
  $this->stow( "#\n" );
274
  $this->stow(
275
- '# ' . sprintf(
276
- __( 'Data contents of table %s', 'search-and-replace' ),
277
- $this->backquote( $new_table )
278
- ) . "\n"
279
  );
280
  $this->stow( "#\n" );
281
 
282
- $defs = array();
283
- $ints = array();
284
  foreach ( $table_structure as $struct ) {
285
- if ( ( 0 === strpos( $struct->Type, 'tinyint' ) )
286
- || ( 0 === strpos( strtolower( $struct->Type ), 'smallint' ) )
287
- || ( 0 === strpos( strtolower( $struct->Type ), 'mediumint' ) )
288
- || ( 0 === strpos( strtolower( $struct->Type ), 'int' ) )
289
- || ( 0 === strpos( strtolower( $struct->Type ), 'bigint' ) )
290
  ) {
291
- $defs[ strtolower( $struct->Field ) ] = ( NULL === $struct->Default ) ? 'NULL' : $struct->Default;
292
  $ints[ strtolower( $struct->Field ) ] = '1';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  }
294
  }
295
 
296
- //split columns array in primary key string and columns array
297
  $columns = $this->dbm->get_columns( $table );
298
- $primary_key = $columns[ 0 ];
299
-
300
- $row_count = $this->dbm->get_rows( $table );
301
-
302
- $page_size = $this->page_size;
303
- $pages = ceil( $row_count / $page_size );
 
 
 
 
 
 
 
 
304
 
305
  for ( $page = 0; $page < $pages; $page ++ ) {
306
  $start = $page * $page_size;
@@ -308,66 +440,105 @@ class Exporter {
308
  $table_data = $this->dbm->get_table_content( $table, $start, $page_size );
309
 
310
  $entries = 'INSERT INTO ' . $this->backquote( $new_table ) . ' VALUES (';
311
- // \x08\\x09, not required
312
- $hex_search = array( "\x00", "\x0a", "\x0d", "\x1a" );
313
- $hex_replace = array( '\0', '\n', '\r', '\Z' );
314
- if ( $table_data ) {
315
- foreach ( $table_data as $row ) {
316
- $values = array();
317
- $table_report[ 'rows' ] ++;
318
-
319
- foreach ( $row as $column => $value ) {
320
- //if "change database prefix" is set we have to look for occurrences of the old prefix in the db entries and change them
 
 
321
  if ( $new_table !== $table ) {
322
- $value = $this->replace->recursive_unserialize_replace(
323
- $table_prefix, $new_table_prefix,
324
- $value
325
- );
 
 
 
 
 
 
 
 
326
  }
327
- //skip replace if no search pattern
328
- //check if we need to replace something
329
- //skip primary_key
330
- if ( $search !== '' && $column !== $primary_key ) {
331
 
332
- $edited_data = $this->replace->recursive_unserialize_replace(
333
- $search, $replace,
334
- $value
335
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
 
337
- // Something was changed
338
- if ( $edited_data !== $value ) {
 
 
 
 
 
 
 
 
 
339
 
340
- $table_report[ 'change' ] ++;
 
 
341
 
342
  // log changes
343
-
344
- $table_report[ 'changes' ][] = array(
345
- 'row' => $table_report[ 'rows' ],
346
  'column' => $column,
347
  'from' => $value,
348
  'to' => $edited_data,
349
- );
350
- $value = $edited_data;
351
 
 
352
  }
353
-
354
  }
 
355
  if ( isset( $ints[ strtolower( $column ) ] ) ) {
356
  // make sure there are no blank spots in the insert syntax,
357
  // yet try to avoid quotation marks around integers
358
- $value = ( NULL === $value || '' === $value ) ? $defs[ strtolower( $column ) ] : $value;
359
  $values[] = ( '' === $value ) ? "''" : $value;
 
 
 
360
  } else {
361
  $values[] = "'" . str_replace(
362
- $hex_search, $hex_replace,
 
363
  $this->sql_addslashes( $value )
364
  ) . "'";
365
  }
366
- }
 
367
  $this->stow( " \n" . $entries . implode( ', ', $values ) . ');' );
368
- }
369
 
370
- }
 
371
  }
372
 
373
  // Create footer/closing comment in SQL-file
@@ -375,6 +546,7 @@ class Exporter {
375
  $this->stow( "#\n" );
376
  $this->stow(
377
  '# ' . sprintf(
 
378
  __( 'End of data contents of table %s', 'search-and-replace' ),
379
  $this->backquote( $new_table )
380
  ) . "\n"
@@ -383,7 +555,28 @@ class Exporter {
383
  $this->stow( "\n" );
384
 
385
  return $table_report;
 
386
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  }
388
 
389
  /**
@@ -391,11 +584,11 @@ class Exporter {
391
  * Taken from phpMyAdmin.
392
  *
393
  * @param string $a_string
394
- * @param bool $is_like
395
  *
396
  * @return mixed
397
  */
398
- protected function sql_addslashes( $a_string = '', $is_like = FALSE ) {
399
 
400
  if ( $is_like ) {
401
  $a_string = str_replace( '\\', '\\\\\\\\', $a_string );
@@ -407,181 +600,114 @@ class Exporter {
407
  }
408
 
409
  /**
410
- * Add backquotes to tables and db-names in
411
- * SQL queries. Taken from phpMyAdmin.
412
  *
413
- * @param $a_name
414
- *
415
- * @return array|string
416
  */
417
- protected function backquote( $a_name ) {
418
-
419
- if ( ! empty( $a_name ) && $a_name !== '*' ) {
420
- if ( is_array( $a_name ) ) {
421
- $result = array();
422
- reset( $a_name );
423
- while ( list( $key, $val ) = each( $a_name ) ) {
424
- $result[ $key ] = '`' . $val . '`';
425
- }
426
-
427
- return $result;
428
- } else {
429
- return '`' . $a_name . '`';
430
- }
431
- } else {
432
- return $a_name;
433
- }
434
- }
435
-
436
- protected function open( $filename = '', $mode = 'w' ) {
437
-
438
- if ( '' === $filename ) {
439
- return FALSE;
440
- }
441
-
442
- return @fopen( $filename, $mode );
443
- }
444
-
445
- protected function close( $fp ) {
446
 
447
  fclose( $fp );
448
  }
449
 
450
  /**
451
- * writes a line to the backup file
452
  *
453
- * @param $query_line
454
- */
455
- protected function stow( $query_line ) {
456
-
457
- if ( @fwrite( $this->fp, $query_line ) === FALSE ) {
458
- $this->errors->add(
459
- 4,
460
- esc_attr__( 'There was an error writing a line to the backup script:', 'search-and-replace' )
461
- . ' ' . $query_line . ' ' . $php_errormsg
462
- );
463
- }
464
- }
465
-
466
- /**
467
- * @param string $filename The name of the file to be downloaded
468
- * @param bool $compress If TRUE, gz compression is used
469
  *
470
  * @return bool FALSE if error , has to DIE when file is delivered
471
  */
472
- public function deliver_backup( $filename = '', $compress = FALSE ) {
473
 
474
  if ( '' === $filename ) {
475
- return FALSE;
476
  }
477
 
 
478
  $diskfile = $this->backup_dir . $filename;
479
- //compress file if set
480
- if ( $compress ) {
481
- $gz_diskfile = "{$diskfile}.gz";
482
-
483
- /**
484
- * Try upping the memory limit before gzipping
485
- */
486
- if ( function_exists( 'memory_get_usage' ) && ( (int) @ini_get( 'memory_limit' ) < 64 ) ) {
487
- @ini_set( 'memory_limit', '64M' );
488
- }
489
-
490
- if ( file_exists( $diskfile ) ) {
491
- /**
492
- * Try gzipping with an external application
493
- */
494
- if ( ! file_exists( $gz_diskfile ) ) {
495
- @exec( "gzip $diskfile" );
496
- }
497
 
498
- if ( file_exists( $gz_diskfile ) ) {
499
-
500
- unlink( $diskfile );
501
-
502
- $diskfile = $gz_diskfile;
503
- $filename = "{$filename}.gz";
504
-
505
- /**
506
- * Try to compress to gzip, if available
507
- */
508
- } else {
509
- if ( function_exists( 'gzencode' ) ) {
510
- if ( function_exists( 'file_get_contents' ) ) {
511
- $text = file_get_contents( $diskfile );
512
- } else {
513
- $text = implode( '', file( $diskfile ) );
514
- }
515
- $gz_text = gzencode( $text, 9 );
516
- $fp = fopen( $gz_diskfile, 'w' );
517
- fwrite( $fp, $gz_text );
518
- if ( fclose( $fp ) ) {
519
- unlink( $diskfile );
520
- $diskfile = $gz_diskfile;
521
- $filename = "{$filename}.gz";
522
- }
523
- }
524
- }
525
- /*
526
- *
527
- */
528
- } elseif ( file_exists( $gz_diskfile ) ) {
529
- $diskfile = $gz_diskfile;
530
- $filename = "{$filename}.gz";
531
- }
532
  }
533
 
534
- //provide file for download
535
- if ( file_exists( $diskfile ) ) {
536
- header( 'Content-Type: application/force-download' );
537
- header( 'Content-Type: application/octet-stream' );
538
- header( 'Content-Length: ' . filesize( $diskfile ) );
539
- header( 'Content-Disposition: attachment; filename=' . $filename );
540
- $success = readfile( $diskfile );
541
- if ( $success ) {
542
- unlink( $diskfile );
543
- die();
544
- }
545
  }
546
 
547
- }
 
 
 
 
548
 
549
- /**
550
- * @return string
551
- */
552
- public function get_backup_dir() {
553
 
554
- return $this->backup_dir;
555
  }
556
 
557
  /**
558
- * @string $backup_dir
559
- * @param $backup_dir
 
560
  */
561
- public function set_backup_dir( $backup_dir ) {
562
 
563
- $this->backup_dir = $backup_dir;
 
 
 
564
  }
565
 
566
  /**
567
- * strips the current table prefix and adds a new one provided in $new_table_prefix
568
  *
569
- * @param $table
570
- * @param $new_table_prefix
571
  *
572
- * @return string The table name with new prefix
573
  */
574
- protected function get_new_table_name( $table, $new_table_prefix ) {
575
 
576
- //get length of base_prefix
577
- $prefix = $this->dbm->get_base_prefix();
578
- $prefix_length = strlen( $prefix );
579
- //strip old prefix
580
- $part_after_prefix = substr( $table, $prefix_length );
581
 
582
- #//build new table name
583
- return $new_table_prefix . $part_after_prefix;
584
- }
585
 
 
 
 
 
 
 
586
 
587
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @noinspection SqlNoDataSourceInspection */
2
 
3
  namespace Inpsyde\SearchReplace\Database;
4
 
5
  /**
6
  * Class Exporter
7
  *
8
+ * @property bool|resource fb
9
  * @package Inpsyde\SearchReplace\Database
10
  */
11
  class Exporter {
12
 
13
  /**
14
+ * Stores all error messages in a WP_Error Object
15
  */
16
+ private $errors;
17
 
18
  /**
19
  * @string The Path to the Backup Directory
20
  */
21
+ private $backup_dir;
22
 
23
  /**
24
  * @var Replace
25
  */
26
+ private $replace;
27
 
28
  /**
29
  * @var Manager
30
  */
31
+ private $dbm;
32
 
33
  /**
34
  * Count of rows to be replaced at a time
35
  *
36
  * @var int
37
  */
38
+ private $page_size = 100;
 
39
 
40
  /**
41
+ * Stores the filename of the backup file
42
  */
43
+ private $backup_filename;
44
 
45
  /**
46
  * Store file path.
47
  *
48
  * @var $fp
49
  */
50
+ private $fp;
51
+
52
+ /**
53
+ * Store csv data
54
+ *
55
+ * @var array
56
+ */
57
+ private $csv_data = [];
58
 
59
  /**
60
  * Exporter constructor.
61
  *
62
  * @param Replace $replace
63
  * @param Manager $dbm
64
+ * @param \WP_Error $wp_error
65
  */
66
+ public function __construct( Replace $replace, Manager $dbm, \WP_Error $wp_error ) {
67
 
68
+ $this->errors = $wp_error;
69
  $this->backup_dir = get_temp_dir();
70
  $this->replace = $replace;
71
  $this->dbm = $dbm;
76
  *
77
  * @param string $search
78
  * @param string $replace
79
+ * @param array $tables The array of table names that should be exported.
80
+ * @param bool $domain_replace If set, exporter will change the domain name without leading http:// in table
81
  * wp_blogs if we are on a multisite
82
+ * @param string $new_table_prefix
83
+ * @param null $csv
84
  *
85
  * @return array $report $report [ 'filename'] : Name of Backup file,
86
  * $report[ 'errors'] : WP_Error_object,
87
  * $report ['changes'] : Array with replacements in tables
88
+ * @throws \Throwable
89
  */
90
+ public function db_backup(
91
+ $search = '',
92
+ $replace = '',
93
+ $tables = [],
94
+ $domain_replace = false,
95
+ $new_table_prefix = '',
96
+ $csv = null
97
+ ) {
98
 
99
  if ( count( $tables ) < 1 ) {
100
  $tables = $this->dbm->get_tables();
101
  }
102
 
103
+ $report = [
104
+ 'errors' => null,
105
+ 'changes' => [],
106
  'tables' => '0',
107
  'changes_count' => '0',
108
  'filename' => '',
109
+ ];
110
 
111
  $table_prefix = $this->dbm->get_base_prefix();
112
 
113
  // wp_blogs needs special treatment in multisite domain replace, we need to check later if we are working on it.
114
  $wp_blogs_table = $table_prefix . 'blogs';
115
 
116
+ $this->backup_filename = $new_table_prefix === '' ?
117
+ DB_NAME . "_$table_prefix.sql" :
118
+ DB_NAME . "_$new_table_prefix.sql";
119
 
120
+ // If the directory for the backup isn't writable, don't proceed.
121
+ // @ToDo Use WP_Filesystem() to access to the file system.
122
+ if ( ! is_writable( $this->backup_dir ) ) {
 
 
 
 
 
 
 
123
  $this->errors->add( 9, esc_attr__( 'The backup directory is not writable!', 'search-and-replace' ) );
124
 
125
  return $report;
126
  }
127
 
128
+ $this->fp = $this->open( $this->backup_dir . $this->backup_filename );
129
+
130
+ if ( ! $this->fp ) {
131
+ $this->errors->add(
132
+ 8,
133
+ esc_attr__( 'Could not open the backup file for writing!', 'search-and-replace' )
134
+ );
135
+
136
+ return $report;
137
+ }
138
+
139
+ // Begin new backup of MySql get charset. if not set assume utf8.
140
+ $charset = ( defined( 'DB_CHARSET' ) ? DB_CHARSET : 'utf8mb4' );
141
+
142
  $this->stow( '# ' . esc_attr__( 'WordPress MySQL database backup', 'search-and-replace' ) . "\n" );
143
  $this->stow( "#\n" );
144
  $this->stow( '# ' . sprintf( __( 'Generated: %s', 'search-and-replace' ), date( 'l j. F Y H:i T' ) ) . "\n" );
145
  $this->stow( '# ' . sprintf( __( 'Hostname: %s', 'search-and-replace' ), DB_HOST ) . "\n" );
146
  $this->stow( '# ' . sprintf( __( 'Database: %s', 'search-and-replace' ), $this->backquote( DB_NAME ) ) . "\n" );
147
+
148
  if ( '' !== $new_table_prefix ) {
149
  $this->stow(
150
  '# ' . sprintf(
151
+ /* translators: $1 and $2 are the name of the database. */
152
+ __( 'Changed table prefix: From %1$s to %2$s ', 'search-and-replace' ),
153
  $table_prefix,
154
  $new_table_prefix
155
  )
156
  . "\n"
157
  );
158
  }
 
159
 
160
+ $this->stow( "# --------------------------------------------------------\n" );
161
  $this->stow( "/*!40101 SET NAMES $charset */;\n" );
162
  $this->stow( "# --------------------------------------------------------\n" );
163
+
164
  foreach ( $tables as $table ) {
165
 
166
+ // Count tables.
167
+ $report ['tables'] ++;
168
 
169
  /**
170
  * Check if we are replacing the domain in a multisite.
171
  * If so, we replace in wp_blogs the stripped url without http(s), because the domains
172
  * are stored without http://
173
  */
174
+ if ( $table === $wp_blogs_table && $domain_replace && is_multisite() ) {
 
175
  $stripped_url_search = substr( $search, strpos( $search, '/' ) + 2 );
176
  $stripped_url_replace = substr( $replace, strpos( $replace, '/' ) + 2 );
177
+
178
+ // Backup table.
179
+ $table_report = $this->backup_table(
180
  $stripped_url_search,
181
  $stripped_url_replace,
182
  $table,
183
  $new_table_prefix
184
  );
 
185
  } else {
186
+ // Backup table.
187
+ $table_report = $this->backup_table( $search, $replace, $table, $new_table_prefix, $csv );
188
  }
 
189
 
190
+ // Log changes if any.
191
+ if ( 0 !== $table_report['change'] ) {
192
+ $report['changes'][ $table ] = $table_report;
193
 
194
+ $report ['changes_count'] += $table_report['change'];
195
  }
196
  }
197
 
198
  $this->close( $this->fp );
199
+
200
+ // Return errors if any.
201
  if ( count( $this->errors->get_error_codes() ) ) {
202
+ $report['errors'] = $this->errors;
203
  }
204
+
205
+ $report ['filename'] = $this->backup_filename;
206
 
207
  return $report;
208
+ }
209
+
210
+ /**
211
+ * Open Resource
212
+ *
213
+ * @param string $filename
214
+ * @param string $mode
215
+ *
216
+ * @return bool|resource
217
+ */
218
+ private function open( $filename = '', $mode = 'wb' ) {
219
+
220
+ if ( '' === $filename ) {
221
+ return false;
222
+ }
223
+
224
+ return @fopen( $filename, $mode );
225
+ }
226
 
227
+ /**
228
+ * writes a line to the backup file
229
+ *
230
+ * @param $query_line
231
+ */
232
+ private function stow( $query_line ) {
233
+
234
+ if ( @fwrite( $this->fp, $query_line ) === false ) {
235
+ $this->errors->add(
236
+ 4,
237
+ sprintf(
238
+ esc_attr__(
239
+ 'There was an error writing a line to the backup script: %s',
240
+ 'search-and-replace'
241
+ ),
242
+ (int) $query_line
243
+ )
244
+ );
245
+ }
246
  }
247
 
248
  /**
249
+ * Back Quote
250
+ *
251
+ * Add backquotes to tables and db-names in
252
+ * SQL queries. Taken from phpMyAdmin.
253
+ *
254
+ * @param $a_name
255
+ *
256
+ * @return array|string
257
+ */
258
+ private function backquote( $a_name ) {
259
+
260
+ if ( ! empty( $a_name ) && $a_name !== '*' ) {
261
+ if ( is_array( $a_name ) ) {
262
+ $result = [];
263
+ reset( $a_name );
264
+ foreach ( $a_name as $key => $val ) {
265
+ // while ( list( $key, $val ) = each( $a_name ) ) {
266
+ $result[ $key ] = '`' . $val . '`';
267
+ }
268
+
269
+ return $result;
270
+ }
271
+
272
+ return '`' . $a_name . '`';
273
+ }
274
+
275
+ return $a_name;
276
+ }
277
+
278
+ /**
279
+ * Backup Table
280
+ *
281
  * Taken partially from phpMyAdmin and partially from
282
  * Alain Wolf, Zurich - Switzerland
283
+ *
284
  * Website: http://restkultur.ch/personal/wolf/scripts/db_backup/
285
+ * Modified by Scott Merrill (http://www.skippy.net/) to use the WordPress $wpdb object
 
286
  *
287
  * @param string $search
288
  * @param string $replace
290
  * @param string $new_table_prefix
291
  *
292
  * @return array $table_report Reports the changes made to the db.
293
+ * @throws \Throwable
294
  */
295
+ public function backup_table( $search = '', $replace = '', $table, $new_table_prefix = '', $csv = null ) {
296
 
297
+ $table_report = [
 
 
298
  'table_name' => $table,
299
  'rows' => 0,
300
  'change' => 0,
301
+ 'changes' => [],
302
+ ];
303
+
304
+ // Default columns values.
305
+ $defs = [];
306
+ // Integer value container.
307
+ $ints = [];
308
+
309
+ // This array is storage for maybe_serialized values. We must prevent deserialization of user supplied content.
310
+ $maybe_serialized = [];
311
+ $binaries = [];
312
+
313
+ // Do we need to replace the prefix?
314
  $table_prefix = $this->dbm->get_base_prefix();
315
  $new_table = $table;
316
+
317
  if ( '' !== $new_table_prefix ) {
318
  $new_table = $this->get_new_table_name( $table, $new_table_prefix );
 
319
  }
320
 
321
  // Create the SQL statements
322
  $this->stow( '# --------------------------------------------------------' . "\n" );
323
  $this->stow( '# ' . sprintf( __( 'Table: %s', 'search-and-replace' ), $this->backquote( $new_table ) ) . "\n" );
324
 
325
+ // Retrieve table structure.
326
  $table_structure = $this->dbm->get_table_structure( $table );
327
+
328
  if ( ! $table_structure ) {
329
  $this->errors->add( 1, __( 'Error getting table details', 'search-and-replace' ) . ": $table" );
330
 
358
 
359
  /** @var array $create_table */
360
  $create_table = $this->dbm->get_create_table_statement( $table );
361
+
362
+ if ( false === $create_table ) {
363
+ /* translators: $1 is the name of the table */
364
  $err_msg = sprintf( __( 'Error with SHOW CREATE TABLE for %s.', 'search-and-replace' ), $table );
365
  $this->errors->add( 2, $err_msg );
366
  $this->stow( "#\n# $err_msg\n#\n" );
367
  }
 
 
 
 
368
 
369
+ // Replace prefix if necessary
370
+ if ( '' !== $new_table_prefix ) {
371
+ $create_table[0][1] = str_replace( $table, $new_table, $create_table[0][1] );
372
+ $table_report['new_table_name'] = $new_table;
373
  }
 
374
 
375
+ $this->stow( $create_table[0][1] . ' ;' );
376
+
377
+ if ( false === $table_structure ) {
378
+ /* translators: $1 is the name of the table */
379
  $err_msg = sprintf( __( 'Error getting table structure of %s', 'search-and-replace' ), $table );
380
  $this->errors->add( 3, $err_msg );
381
  $this->stow( "#\n# $err_msg\n#\n" );
385
  $this->stow( "\n\n" );
386
  $this->stow( "#\n" );
387
  $this->stow(
388
+ /* translators: $1 is the name of the new table */
389
+ '# ' . sprintf( __( 'Data contents of table %s', 'search-and-replace' ), $this->backquote( $new_table ) ) .
390
+ "\n"
 
391
  );
392
  $this->stow( "#\n" );
393
 
 
 
394
  foreach ( $table_structure as $struct ) {
395
+ if ( 0 === strpos( $struct->Type, 'tinyint' )
396
+ || 0 === stripos( $struct->Type, 'smallint' )
397
+ || 0 === stripos( $struct->Type, 'mediumint' )
398
+ || 0 === stripos( $struct->Type, 'int' )
399
+ || 0 === stripos( $struct->Type, 'bigint' )
400
  ) {
401
+ $defs[ strtolower( $struct->Field ) ] = ( null === $struct->Default ) ? 'NULL' : $struct->Default;
402
  $ints[ strtolower( $struct->Field ) ] = '1';
403
+
404
+ } elseif ( 0 === stripos( $struct->Type, 'binary' )
405
+ || 0 === stripos( $struct->Type, 'varbinary' )
406
+ || 0 === stripos( $struct->Type, 'blob' )
407
+ || 0 === stripos( $struct->Type, 'tinyblob' )
408
+ || 0 === stripos( $struct->Type, 'mediumblob' )
409
+ || 0 === stripos( $struct->Type, 'longblob' )
410
+ ) {
411
+ $binaries[ strtolower( $struct->Field ) ] = 1;
412
+ }
413
+
414
+ // Longtext is used for meta_values as best practice in all of the automatic products.
415
+ if ( 0 === stripos( $struct->Type, 'longtext' ) ) {
416
+ $maybe_serialized[] = strtolower( $struct->Field );
417
  }
418
  }
419
 
420
+ // Split columns array in primary key string and columns array.
421
  $columns = $this->dbm->get_columns( $table );
422
+ $primary_key = $columns[0];
423
+ $row_count = $this->dbm->get_rows( $table );
424
+ $page_size = $this->page_size;
425
+ $pages = ceil( $row_count / $page_size );
426
+
427
+ // Prepare CSV data.
428
+ if ( $csv !== null ) {
429
+ $csv_lines = explode( "\n", $csv );
430
+ $csv_head = str_getcsv( 'search,replace' );
431
+
432
+ foreach ( $csv_lines as $line ) {
433
+ $this->csv_data[] = array_combine( $csv_head, str_getcsv( $line ) );
434
+ }
435
+ }
436
 
437
  for ( $page = 0; $page < $pages; $page ++ ) {
438
  $start = $page * $page_size;
440
  $table_data = $this->dbm->get_table_content( $table, $start, $page_size );
441
 
442
  $entries = 'INSERT INTO ' . $this->backquote( $new_table ) . ' VALUES (';
443
+ // \x08\\x09, not required
444
+ $hex_search = [ "\x00", "\x0a", "\x0d", "\x1a" ];
445
+ $hex_replace = [ '\0', '\n', '\r', '\Z' ];
446
+
447
+ if ( $table_data ) :
448
+ foreach ( $table_data as $row ) :
449
+ $values = [];
450
+ $table_report['rows'] ++;
451
+
452
+ foreach ( $row as $column => $value ) :
453
+ // If "change database prefix" is set we have to look for occurrences of the old prefix
454
+ // in the db entries and change them.
455
  if ( $new_table !== $table ) {
456
+ // Check if column is expected to hold serialized value.
457
+ if ( is_serialized( $value, false )
458
+ && in_array( strtolower( $column ), $maybe_serialized, true )
459
+ ) {
460
+ $value = $this->replace->recursive_unserialize_replace(
461
+ $table_prefix,
462
+ $new_table_prefix,
463
+ $value
464
+ );
465
+ } else {
466
+ $value = str_replace( $table_prefix, $new_table_prefix, $value );
467
+ }
468
  }
 
 
 
 
469
 
470
+ // Skip replace if no search pattern
471
+ // Check if we need to replace something
472
+ // Skip primary_key
473
+ // Skip `guid` column https://codex.wordpress.org/Changing_The_Site_URL#Important_GUID_Note
474
+ if ( $column !== $primary_key && $column !== 'guid' ) {
475
+ // Initialize
476
+ $edited_data = '';
477
+
478
+ if ( '' !== $search ) {
479
+ // Check if column is expected to hold serialized value.
480
+ if ( is_serialized( $value, false )
481
+ && in_array( strtolower( $column ), $maybe_serialized, true )
482
+ ) {
483
+ $edited_data = $this->replace->recursive_unserialize_replace(
484
+ $search,
485
+ $replace,
486
+ $value
487
+ );
488
+ } else {
489
+ $edited_data = str_replace( $search, $replace, $value );
490
+ }
491
+ }
492
 
493
+ // If csv string has passed let's replace those values.
494
+ if ( $csv !== null ) {
495
+ foreach ( $this->csv_data as $entry ) {
496
+ $edited_data = is_serialized( $edited_data, false ) ?
497
+ $this->replace->recursive_unserialize_replace(
498
+ $entry['search'],
499
+ $entry['replace'],
500
+ $edited_data
501
+ ) : str_replace( $entry['search'], $entry['replace'], $value );
502
+ }
503
+ }
504
 
505
+ // When a replace happen, update the table report.
506
+ if ( $edited_data && $edited_data !== $value ) {
507
+ $table_report['change'] ++;
508
 
509
  // log changes
510
+ $table_report['changes'][] = [
511
+ 'row' => $table_report['rows'],
 
512
  'column' => $column,
513
  'from' => $value,
514
  'to' => $edited_data,
515
+ ];
 
516
 
517
+ $value = $edited_data;
518
  }
 
519
  }
520
+
521
  if ( isset( $ints[ strtolower( $column ) ] ) ) {
522
  // make sure there are no blank spots in the insert syntax,
523
  // yet try to avoid quotation marks around integers
524
+ $value = ( null === $value || '' === $value ) ? $defs[ strtolower( $column ) ] : $value;
525
  $values[] = ( '' === $value ) ? "''" : $value;
526
+ } else if ( isset( $binaries[ strtolower( $column ) ] ) ) {
527
+ $hex = unpack( 'H*', $value );
528
+ $values[] = "0x$hex[1]";
529
  } else {
530
  $values[] = "'" . str_replace(
531
+ $hex_search,
532
+ $hex_replace,
533
  $this->sql_addslashes( $value )
534
  ) . "'";
535
  }
536
+ endforeach;
537
+
538
  $this->stow( " \n" . $entries . implode( ', ', $values ) . ');' );
 
539
 
540
+ endforeach;
541
+ endif;
542
  }
543
 
544
  // Create footer/closing comment in SQL-file
546
  $this->stow( "#\n" );
547
  $this->stow(
548
  '# ' . sprintf(
549
+ /* translators: $1 is the name of the table */
550
  __( 'End of data contents of table %s', 'search-and-replace' ),
551
  $this->backquote( $new_table )
552
  ) . "\n"
555
  $this->stow( "\n" );
556
 
557
  return $table_report;
558
+ }
559
 
560
+ /**
561
+ * Get new Table name
562
+ *
563
+ * strips the current table prefix and adds a new one provided in $new_table_prefix
564
+ *
565
+ * @param $table
566
+ * @param $new_table_prefix
567
+ *
568
+ * @return string The table name with new prefix
569
+ */
570
+ private function get_new_table_name( $table, $new_table_prefix ) {
571
+
572
+ // Get length of base_prefix
573
+ $prefix = $this->dbm->get_base_prefix();
574
+ $prefix_length = strlen( $prefix );
575
+ // Strip old prefix
576
+ $part_after_prefix = substr( $table, $prefix_length );
577
+
578
+ // Build new table name
579
+ return $new_table_prefix . $part_after_prefix;
580
  }
581
 
582
  /**
584
  * Taken from phpMyAdmin.
585
  *
586
  * @param string $a_string
587
+ * @param bool $is_like
588
  *
589
  * @return mixed
590
  */
591
+ private function sql_addslashes( $a_string = '', $is_like = false ) {
592
 
593
  if ( $is_like ) {
594
  $a_string = str_replace( '\\', '\\\\\\\\', $a_string );
600
  }
601
 
602
  /**
603
+ * Close Resource
 
604
  *
605
+ * @param $fp
 
 
606
  */
607
+ private function close( $fp ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
608
 
609
  fclose( $fp );
610
  }
611
 
612
  /**
613
+ * Deliver
614
  *
615
+ * @param string $filename The name of the file to be downloaded.
616
+ * @param bool $compress If TRUE, gz compression is used.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
617
  *
618
  * @return bool FALSE if error , has to DIE when file is delivered
619
  */
620
+ public function deliver_backup( $filename = '', $compress = false ) {
621
 
622
  if ( '' === $filename ) {
623
+ return false;
624
  }
625
 
626
+ // Build the file path.
627
  $diskfile = $this->backup_dir . $filename;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
628
 
629
+ // Let know the user why we cannot download his file.
630
+ if ( ! file_exists( $diskfile ) ) {
631
+ wp_die(
632
+ esc_html__( 'Seems was not possible to create the file for some reason.', 'search-and-replace' ),
633
+ esc_html__( 'Cannot Process the file - Search &amp; Replace', 'search-and-replace' ),
634
+ [
635
+ 'back_link' => true,
636
+ ]
637
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
638
  }
639
 
640
+ // Compress file if set.
641
+ if ( $compress ) {
642
+ // Gzipping may eat into memory.
643
+ $this->increase_memory();
644
+
645
+ $diskfile = $this->gzip( $diskfile );
 
 
 
 
 
646
  }
647
 
648
+ // Provide file for download.
649
+ header( 'Content-Type: application/force-download' );
650
+ header( 'Content-Type: application/octet-stream' );
651
+ header( 'Content-Length: ' . filesize( $diskfile ) );
652
+ header( 'Content-Disposition: attachment; filename=' . basename( $diskfile ) );
653
 
654
+ readfile( $diskfile );
 
 
 
655
 
656
+ die();
657
  }
658
 
659
  /**
660
+ * Increase Memory
661
+ *
662
+ * @return void
663
  */
664
+ private function increase_memory() {
665
 
666
+ // Try upping the memory limit before gzipping.
667
+ if ( function_exists( 'memory_get_usage' ) && ( (int) @ini_get( 'memory_limit' ) < 64 ) ) {
668
+ @ini_set( 'memory_limit', '64M' );
669
+ }
670
  }
671
 
672
  /**
673
+ * Gzip
674
  *
675
+ * @param string $diskfile The path of the file to compress
 
676
  *
677
+ * @return string the file path compressed or not
678
  */
679
+ private function gzip( $diskfile ) {
680
 
681
+ // The file to serve.
682
+ $gz_diskfile = "{$diskfile}.gz";
 
 
 
683
 
684
+ // Always serve a fresh file.
685
+ // If file all-ready exists doesn't mean we have the same replace request.
686
+ file_exists( $gz_diskfile ) && unlink( $gz_diskfile );
687
 
688
+ // Try gzipping with an external application.
689
+ @exec( "gzip $diskfile" );
690
+
691
+ if ( file_exists( $gz_diskfile ) ) {
692
+ $diskfile = $gz_diskfile;
693
+ }
694
 
695
+ // If we are not capable of using `gzip` command, lets try something else.
696
+ if ( $diskfile !== $gz_diskfile && function_exists( 'gzencode' ) ) {
697
+ $text = file_get_contents( $diskfile );
698
+ $gz_text = gzencode( $text, 9 );
699
+ $this->fb = fopen( $gz_diskfile, 'wb' );
700
+
701
+ fwrite( $this->fp, $gz_text );
702
+
703
+ // Don't serve gzipped file if actually we encounter problem to close it.
704
+ if ( fclose( $this->fp ) ) {
705
+ unlink( $diskfile );
706
+
707
+ $diskfile = $gz_diskfile;
708
+ }
709
+ }
710
+
711
+ return $diskfile;
712
+ }
713
+ }
inc/Database/Importer.php CHANGED
@@ -1,6 +1,8 @@
1
  <?php
2
  namespace Inpsyde\SearchReplace\Database;
3
 
 
 
4
  /**
5
  * Class Importer
6
  *
@@ -9,7 +11,21 @@ namespace Inpsyde\SearchReplace\Database;
9
  class Importer {
10
 
11
  /**
12
- * imports a sql file via mysqli
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  *
14
  * @param string $sql
15
  *
@@ -17,7 +33,9 @@ class Importer {
17
  */
18
  public function import_sql( $sql ) {
19
 
20
- //connect via mysqli for easier db import
 
 
21
  $mysqli = new \mysqli( DB_HOST, DB_USER, DB_PASSWORD, DB_NAME );
22
 
23
  // Run the SQL
@@ -38,7 +56,9 @@ class Importer {
38
 
39
  mysqli_close( $mysqli );
40
 
 
 
41
  return $i;
42
  }
43
 
44
- }
1
  <?php
2
  namespace Inpsyde\SearchReplace\Database;
3
 
4
+ use Inpsyde\SearchReplace\Service\MaxExecutionTime;
5
+
6
  /**
7
  * Class Importer
8
  *
11
  class Importer {
12
 
13
  /**
14
+ * @var MaxExecutionTime
15
+ */
16
+ private $max_execution;
17
+
18
+ /**
19
+ * Importer constructor.
20
+ *
21
+ * @param MaxExecutionTime $max_execution
22
+ */
23
+ public function __construct( MaxExecutionTime $max_execution ) {
24
+ $this->max_execution = $max_execution;
25
+ }
26
+
27
+ /**
28
+ * Imports a sql file via mysqli
29
  *
30
  * @param string $sql
31
  *
33
  */
34
  public function import_sql( $sql ) {
35
 
36
+ $this->max_execution->set();
37
+
38
+ // connect via mysqli for easier db import
39
  $mysqli = new \mysqli( DB_HOST, DB_USER, DB_PASSWORD, DB_NAME );
40
 
41
  // Run the SQL
56
 
57
  mysqli_close( $mysqli );
58
 
59
+ $this->max_execution->restore();
60
+
61
  return $i;
62
  }
63
 
64
+ }
inc/Database/Manager.php CHANGED
@@ -1,4 +1,7 @@
1
  <?php
 
 
 
2
  namespace Inpsyde\SearchReplace\Database;
3
 
4
  /**
@@ -10,7 +13,7 @@ class Manager {
10
 
11
  /**
12
  * @var \wpdb
13
- * Wordpress Database Class
14
  * some functions adapted from :
15
  * https://github.com/ExpandedFronts/Better-Search-Replace/blob/master/includes/class-bsr-db.php
16
  */
@@ -43,7 +46,9 @@ class Manager {
43
  $tables = $this->wpdb->get_col( "SHOW TABLES LIKE'" . $this->wpdb->base_prefix . "%'" );
44
  } else {
45
  $blog_id = get_current_blog_id();
46
- $tables = $this->wpdb->get_col( "SHOW TABLES LIKE '" . $this->wpdb->base_prefix . absint( $blog_id ) . "\_%'" );
 
 
47
  }
48
 
49
  } else {
@@ -67,10 +72,10 @@ class Manager {
67
  if ( is_array( $tables ) && ! empty( $tables ) ) {
68
 
69
  foreach ( $tables as $table ) {
70
- $size = round( $table[ 'Data_length' ] / 1024, 2 );
 
71
  $sizes[ $table[ 'Name' ] ] = sprintf( __( '(%s KB)', 'search-and-replace' ), $size );
72
  }
73
-
74
  }
75
 
76
  return $sizes;
@@ -81,7 +86,7 @@ class Manager {
81
  *
82
  * @access public
83
  *
84
- * @param $table
85
  *
86
  * @return int
87
  */
@@ -103,7 +108,7 @@ class Manager {
103
  */
104
  public function get_columns( $table ) {
105
 
106
- $primary_key = NULL;
107
  $columns = array();
108
  $fields = $this->wpdb->get_results( 'DESCRIBE ' . $table );
109
 
@@ -120,9 +125,9 @@ class Manager {
120
  }
121
 
122
  /**
123
- * @param $table String The Table Name
124
- * @param $start Int The start row
125
- * @param $end Int Number of Rows to be fetched
126
  *
127
  * @return array|null|object
128
  */
@@ -136,16 +141,16 @@ class Manager {
136
  /**
137
  * Update table.
138
  *
139
- * @param $table
140
- * @param $update_sql
141
- * @param $where_sql
142
  *
143
  * @return false|int
144
  */
145
  public function update( $table, $update_sql, $where_sql ) {
146
 
147
  $sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) .
148
- ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
149
 
150
  return $this->wpdb->query( $sql );
151
  }
@@ -153,7 +158,7 @@ class Manager {
153
  /**
154
  * Get table structure.
155
  *
156
- * @param $table
157
  *
158
  * @return array|null|object
159
  */
@@ -163,9 +168,9 @@ class Manager {
163
  }
164
 
165
  /**
166
- * returns a SQL CREATE TABLE Statement for the table provided in $table
167
  *
168
- * @param $table String The Name of the table we want to create the statement for.
169
  *
170
  * @return string
171
  */
@@ -192,4 +197,4 @@ class Manager {
192
  return $this->wpdb->base_prefix;
193
  }
194
 
195
- }
1
  <?php
2
+ /** @noinspection SqlDialectInspection */
3
+ /** @noinspection SqlNoDataSourceInspection */
4
+
5
  namespace Inpsyde\SearchReplace\Database;
6
 
7
  /**
13
 
14
  /**
15
  * @var \wpdb
16
+ * WordPress Database Class.
17
  * some functions adapted from :
18
  * https://github.com/ExpandedFronts/Better-Search-Replace/blob/master/includes/class-bsr-db.php
19
  */
46
  $tables = $this->wpdb->get_col( "SHOW TABLES LIKE'" . $this->wpdb->base_prefix . "%'" );
47
  } else {
48
  $blog_id = get_current_blog_id();
49
+ $tables = $this->wpdb->get_col(
50
+ "SHOW TABLES LIKE '" . $this->wpdb->base_prefix . absint( $blog_id ) . "\_%'"
51
+ );
52
  }
53
 
54
  } else {
72
  if ( is_array( $tables ) && ! empty( $tables ) ) {
73
 
74
  foreach ( $tables as $table ) {
75
+ $size = round( $table[ 'Data_length' ] / 1024, 2 );
76
+ // Translators: %s is the value of the size in kByte.
77
  $sizes[ $table[ 'Name' ] ] = sprintf( __( '(%s KB)', 'search-and-replace' ), $size );
78
  }
 
79
  }
80
 
81
  return $sizes;
86
  *
87
  * @access public
88
  *
89
+ * @param array|string $table
90
  *
91
  * @return int
92
  */
108
  */
109
  public function get_columns( $table ) {
110
 
111
+ $primary_key = null;
112
  $columns = array();
113
  $fields = $this->wpdb->get_results( 'DESCRIBE ' . $table );
114
 
125
  }
126
 
127
  /**
128
+ * @param string $table The Table Name.
129
+ * @param integer $start The start row.
130
+ * @param integer $end Int Number of Rows to be fetched.
131
  *
132
  * @return array|null|object
133
  */
141
  /**
142
  * Update table.
143
  *
144
+ * @param string $table
145
+ * @param array $update_sql
146
+ * @param array $where_sql
147
  *
148
  * @return false|int
149
  */
150
  public function update( $table, $update_sql, $where_sql ) {
151
 
152
  $sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) .
153
+ ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
154
 
155
  return $this->wpdb->query( $sql );
156
  }
158
  /**
159
  * Get table structure.
160
  *
161
+ * @param string $table
162
  *
163
  * @return array|null|object
164
  */
168
  }
169
 
170
  /**
171
+ * Returns a SQL CREATE TABLE Statement for the table provided in $table.
172
  *
173
+ * @param string $table String The Name of the table we want to create the statement for.
174
  *
175
  * @return string
176
  */
197
  return $this->wpdb->base_prefix;
198
  }
199
 
200
+ }
inc/Database/Replace.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace Inpsyde\SearchReplace\Database;
3
 
4
  use Inpsyde\SearchReplace\Service;
@@ -11,49 +12,47 @@ use Inpsyde\SearchReplace\Service;
11
  * @package Inpsyde\SearchReplace\Database
12
  */
13
  class Replace {
14
-
15
  /**
16
- * the search string
17
  *
18
- * @var
19
  */
20
- protected $search;
21
 
22
  /**
23
- * the replacement string
24
  *
25
- * @var
26
  */
27
- protected $replace;
28
 
29
  /**
30
- * The Database Interface Object
31
- *
32
- * @type Manager
33
- * @var
34
  */
35
- protected $dbm;
36
 
37
  /**
38
- * Count of rows to be replaced at a time
39
- *
40
- * @var int
41
  */
42
- protected $page_size = 100;
43
 
44
  /**
45
- * @var bool - set if dry run
 
 
46
  */
47
- protected $dry_run = TRUE;
48
 
49
  /**
50
  * Replace constructor.
51
  *
52
- * @param Manager $dbm
 
53
  */
54
- public function __construct( Manager $dbm ) {
55
 
56
- $this->dbm = $dbm;
 
57
  }
58
 
59
  /**
@@ -63,75 +62,102 @@ class Replace {
63
  * We split large tables into blocks (size is set via $page_size)when dealing with them to save
64
  * on memory consumption.
65
  *
66
- * @param string $search What we want to replace
67
  * @param string $replace What we want to replace it with.
68
- * @param string $tables The name of the table we want to look at.
 
69
  *
70
- * @return array Collection of information gathered during the run.
 
71
  */
72
 
73
- public function run_search_replace( $search, $replace, $tables ) {
74
 
75
- if ( $search === $replace ){
76
- return new \WP_Error( 'error', __( "Search and replace pattern can't be the same!" ) );
77
  }
78
 
79
- $execution_time = new Service\MaxExecutionTime();
80
- $execution_time->set();
81
 
82
- $report = array(
83
- 'errors' => NULL,
84
- 'changes' => array(),
85
  'tables' => '0',
86
  'changes_count' => '0',
87
- );
88
 
89
  foreach ( (array) $tables as $table ) {
90
- //count tables
91
- $report [ 'tables' ] ++;
92
- $table_report = $this->replace_values( $search, $replace, $table );
93
- //log changes if any
94
-
95
- if ( 0 !== $table_report[ 'change' ] ) {
96
- $report[ 'changes' ][ $table ] = $table_report;
97
-
98
- $report [ 'changes_count' ] += $table_report[ 'change' ];
99
  }
100
-
101
  }
102
 
103
- $execution_time->restore();
104
 
105
  return $report;
106
  }
107
 
108
- public function replace_values( $search = '', $replace = '', $table ) {
 
 
 
 
 
 
 
 
 
 
 
109
 
110
- $table_report = array(
111
  'table_name' => $table,
112
  'rows' => 0,
113
  'change' => 0,
114
- 'changes' => array(),
115
  'updates' => 0,
116
  'start' => microtime(),
117
  'end' => microtime(),
118
- 'errors' => array(),
119
- );
120
 
121
- // check we have a search string, bail if not
122
- if ( empty( $search ) ) {
123
- $table_report[ 'errors' ][] = 'Search string is empty';
124
 
125
  return $table_report;
126
  }
127
- //split columns array in primary key string and columns array
128
- $columns = $this->dbm->get_columns( $table );
129
- $primary_key = $columns[ 0 ];
130
- $columns = $columns[ 1 ];
131
 
132
- if ( NULL === $primary_key ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  array_push(
134
- $table_report[ 'errors' ],
135
  "The table \"{$table}\" has no primary key. Changes will have to be made manually.",
136
  'results'
137
  );
@@ -139,7 +165,7 @@ class Replace {
139
  return $table_report;
140
  }
141
 
142
- $table_report[ 'start' ] = microtime();
143
 
144
  // Count the number of rows we have in the table if large we'll split into blocks, This is a mod from Simon Wheatley
145
  $row_count = $this->dbm->get_rows( $table );
@@ -147,6 +173,15 @@ class Replace {
147
  $page_size = $this->page_size;
148
  $pages = ceil( $row_count / $page_size );
149
 
 
 
 
 
 
 
 
 
 
150
  for ( $page = 0; $page < $pages; $page ++ ) {
151
 
152
  $start = $page * $page_size;
@@ -155,18 +190,21 @@ class Replace {
155
  $data = $this->dbm->get_table_content( $table, $start, $page_size );
156
 
157
  if ( ! $data ) {
158
- $table_report[ 'errors' ][] = 'no data in table ' . $table;
159
  }
160
 
161
  foreach ( $data as $row ) {
 
162
 
163
- $table_report[ 'rows' ] ++;
164
-
165
- $update_sql = array();
166
- $where_sql = array();
167
- $update = FALSE;
168
 
169
  foreach ( $columns as $column ) {
 
 
 
 
170
 
171
  $data_to_fix = $row[ $column ];
172
 
@@ -174,60 +212,72 @@ class Replace {
174
  $where_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $data_to_fix ) . '"';
175
  continue;
176
  }
177
- /* // exclude cols
178
- if ( in_array( $column, $this->exclude_cols ) )
179
- continue;
180
-
181
- // include cols
182
- if ( ! empty( $this->include_cols ) && ! in_array( $column, $this->include_cols ) )
183
- continue;*/
184
 
185
  // Run a search replace on the data that'll respect the serialisation.
186
- $edited_data = $this->recursive_unserialize_replace( $search, $replace, $data_to_fix );
 
 
 
 
 
 
 
187
 
188
- // Something was changed
189
- if ( $edited_data !== $data_to_fix ) {
 
 
 
 
 
 
 
 
 
190
 
191
- $table_report[ 'change' ] ++;
 
 
192
 
193
  // log changes
194
- //TODO : does it work with non UTF-8 encodings?
195
- $table_report[ 'changes' ][] = array(
196
- 'row' => $table_report[ 'rows' ],
197
  'column' => $column,
198
  'from' => $data_to_fix,
199
  'to' => $edited_data,
200
- );
201
 
202
  $update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $edited_data ) . '"';
203
- $update = TRUE;
204
-
205
  }
206
-
207
  }
208
 
209
  // Determine what to do with updates.
210
- if ( TRUE === $this->dry_run ) {
211
- // Don't do anything if a dry run
212
- } elseif ( $update && ! empty( $where_sql ) ) {
213
- // If there are changes to make, run the query.
214
 
 
 
215
  $result = $this->dbm->update( $table, $update_sql, $where_sql );
216
 
217
  if ( ! $result ) {
218
- $table_report[ 'errors' ][] = sprintf(
219
- __( 'Error updating row: %d.', 'search-and-replace' ),
 
220
  $row
221
  );
222
  } else {
223
- $table_report[ 'updates' ] ++;
224
  }
225
-
226
  }
227
  }
228
  }
229
 
230
- $table_report[ 'end' ] = microtime( TRUE );
 
231
  $this->dbm->flush();
232
 
233
  return $table_report;
@@ -235,113 +285,136 @@ class Replace {
235
  }
236
 
237
  /**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
  * Take a serialised array and unserialize it replacing elements as needed and
239
  * unserializing any subordinate arrays and performing the replace on those too.
240
  *
241
- * @param string $from String we're looking to replace.
242
- * @param string $to What we want it to be replaced with
243
- * @param array|string|object $data Used to pass any subordinate arrays back to in.
244
- * @param bool $serialised Does the array passed via $data need serialising.
 
 
 
 
 
 
 
245
  *
246
  * @return array The original array with all elements replaced as needed.
247
  */
248
- public function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialised = FALSE ) {
249
 
250
- // some unserialized data cannot be re-serialised eg. SimpleXMLElements
251
  try {
 
252
 
253
- if ( is_string( $data ) && ( $unserialized = @unserialize( $data ) ) !== FALSE ) {
254
- $data = $this->recursive_unserialize_replace( $from, $to, $unserialized, TRUE );
255
  } elseif ( is_array( $data ) ) {
256
- $_tmp = array();
257
- foreach ( $data as $key => $value ) {
258
- $_tmp[ $key ] = $this->recursive_unserialize_replace( $from, $to, $value, FALSE );
259
  }
260
 
261
  $data = $_tmp;
 
262
  unset( $_tmp );
263
- } // Submitted by Tina Matter
264
- elseif ( is_object( $data ) ) {
265
- // $data_class = get_class( $data );
266
- $_tmp = $data; // new $data_class( );
267
  $props = get_object_vars( $data );
268
  foreach ( $props as $key => $value ) {
269
- $_tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, FALSE );
270
  }
271
 
272
  $data = $_tmp;
 
273
  unset( $_tmp );
274
  } else {
275
- if ( is_string( $data ) ) {
276
- $data = str_replace( $from, $to, $data );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
 
 
 
 
 
 
 
 
 
278
  }
279
  }
280
 
281
  if ( $serialised ) {
282
- return serialize( $data );
 
 
 
 
 
283
  }
284
 
285
- }
286
- catch ( Exception $error ) {
287
-
288
- $this->add_error( $error->getMessage(), 'results' );
289
-
290
  }
291
 
292
  return $data;
293
  }
294
 
295
  /**
296
- * Checks if the submitted string is a JSON object
297
- *
298
- * @param $string
299
- * @param bool|FALSE $strict
300
  *
301
  * @return bool
302
  */
 
303
 
304
- protected function is_json( $string, $strict = FALSE ) {
305
-
306
- $json = @json_decode( $string, TRUE );
307
- if ( $strict === TRUE && ! is_array( $json ) ) {
308
- return FALSE;
309
- }
310
-
311
- return ! ( $json === NULL || $json === FALSE );
312
- }
313
-
314
- /**
315
- * Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
316
- *
317
- * @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
318
- * @access public
319
- *
320
- * @param array|string $input The string to escape.
321
- *
322
- * @return string
323
- */
324
-
325
- public function mysql_escape_mimic( $input ) {
326
-
327
- if ( is_array( $input ) ) {
328
- return array_map( __METHOD__, $input );
329
- }
330
- if ( ! empty( $input ) && is_string( $input ) ) {
331
- return str_replace(
332
- array( '\\', "\0", "\n", "\r", "'", '"', "\x1a" ),
333
- array( '\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z' ),
334
- $input
335
- );
336
- }
337
-
338
- return $input;
339
  }
340
 
341
  /**
342
  * Sets the dry run option.
343
  *
344
- * @param bool $state : TRUE for dry run, FALSE for writing changes to DB
345
  *
346
  * @return bool
347
  */
@@ -354,15 +427,4 @@ class Replace {
354
 
355
  return $state;
356
  }
357
-
358
- /**
359
- * Returns true, if dry run, false if not
360
- *
361
- * @return bool
362
- */
363
- public function get_dry_run() {
364
-
365
- return $this->dry_run;
366
- }
367
-
368
- }
1
  <?php
2
+
3
  namespace Inpsyde\SearchReplace\Database;
4
 
5
  use Inpsyde\SearchReplace\Service;
12
  * @package Inpsyde\SearchReplace\Database
13
  */
14
  class Replace {
 
15
  /**
16
+ * The Database Interface Object
17
  *
18
+ * @var Manager
19
  */
20
+ private $dbm;
21
 
22
  /**
23
+ * Count of rows to be replaced at a time
24
  *
25
+ * @var int
26
  */
27
+ private $page_size = 100;
28
 
29
  /**
30
+ * @var bool - set if dry run
 
 
 
31
  */
32
+ private $dry_run = true;
33
 
34
  /**
35
+ * @var Service\MaxExecutionTime
 
 
36
  */
37
+ private $max_execution;
38
 
39
  /**
40
+ * Store csv data
41
+ *
42
+ * @var array
43
  */
44
+ private $csv_data = [];
45
 
46
  /**
47
  * Replace constructor.
48
  *
49
+ * @param Manager $dbm
50
+ * @param Service\MaxExecutionTime $max_execution
51
  */
52
+ public function __construct( Manager $dbm, Service\MaxExecutionTime $max_execution ) {
53
 
54
+ $this->dbm = $dbm;
55
+ $this->max_execution = $max_execution;
56
  }
57
 
58
  /**
62
  * We split large tables into blocks (size is set via $page_size)when dealing with them to save
63
  * on memory consumption.
64
  *
65
+ * @param string $search What we want to replace.
66
  * @param string $replace What we want to replace it with.
67
+ * @param string $tables The name of the table we want to look at.
68
+ * @param null $csv
69
  *
70
+ * @return array|\WP_Error Collection of information gathered during the run.
71
+ * @throws \Throwable
72
  */
73
 
74
+ public function run_search_replace( $search, $replace, $tables, $csv = null ) {
75
 
76
+ if ( $search === $replace && '' !== $search ) {
77
+ return new \WP_Error( 'error', esc_html__( 'Search and replace pattern can\'t be the same!', 'search-and-replace' ) );
78
  }
79
 
80
+ $this->max_execution->set();
 
81
 
82
+ $report = [
83
+ 'errors' => null,
84
+ 'changes' => [],
85
  'tables' => '0',
86
  'changes_count' => '0',
87
+ ];
88
 
89
  foreach ( (array) $tables as $table ) {
90
+ // Count tables.
91
+ $report ['tables'] ++;
92
+ $table_report = $this->replace_values( $search, $replace, $table, $csv );
93
+ // Log changes if any.
94
+ if ( 0 !== $table_report['change'] ) {
95
+ $report['changes'][ $table ] = $table_report;
96
+ $report ['changes_count'] += $table_report['change'];
 
 
97
  }
 
98
  }
99
 
100
+ $this->max_execution->restore();
101
 
102
  return $report;
103
  }
104
 
105
+ /**
106
+ * Replace data values inside the table.
107
+ *
108
+ * @param string $search
109
+ * @param string $replace
110
+ * @param string $table
111
+ * @param null $csv
112
+ *
113
+ * @return array
114
+ * @throws \Throwable
115
+ */
116
+ public function replace_values( $search = '', $replace = '', $table, $csv = null ) {
117
 
118
+ $table_report = [
119
  'table_name' => $table,
120
  'rows' => 0,
121
  'change' => 0,
122
+ 'changes' => [],
123
  'updates' => 0,
124
  'start' => microtime(),
125
  'end' => microtime(),
126
+ 'errors' => [],
127
+ ];
128
 
129
+ // Check we have a search string, bail if not.
130
+ if ( empty( $search ) && empty( $csv ) ) {
131
+ $table_report['errors'][] = 'Search string is empty';
132
 
133
  return $table_report;
134
  }
 
 
 
 
135
 
136
+ // Grab table structure in order to determine which columns are used to store serialized values in it.
137
+ $table_structure = $this->dbm->get_table_structure( $table );
138
+
139
+ if ( ! $table_structure ) {
140
+ return $table_report;
141
+ }
142
+
143
+ $maybe_serialized = [];
144
+ foreach ( $table_structure as $struct ) {
145
+ // Longtext is used for meta_values as best practice in all of the automatic products.
146
+ // @codingStandardsIgnoreLine
147
+ if ( 0 === stripos( $struct->Type, 'longtext' ) ) {
148
+ // @codingStandardsIgnoreLine
149
+ $maybe_serialized[] = strtolower( $struct->Field );
150
+ }
151
+ }
152
+
153
+ // Split columns array in primary key string and columns array.
154
+ $columns = $this->dbm->get_columns( $table );
155
+
156
+ list( $primary_key, $columns ) = $columns;
157
+
158
+ if ( null === $primary_key ) {
159
  array_push(
160
+ $table_report['errors'],
161
  "The table \"{$table}\" has no primary key. Changes will have to be made manually.",
162
  'results'
163
  );
165
  return $table_report;
166
  }
167
 
168
+ $table_report['start'] = microtime();
169
 
170
  // Count the number of rows we have in the table if large we'll split into blocks, This is a mod from Simon Wheatley
171
  $row_count = $this->dbm->get_rows( $table );
173
  $page_size = $this->page_size;
174
  $pages = ceil( $row_count / $page_size );
175
 
176
+ // Prepare CSV data
177
+ if ( null !== $csv ) {
178
+ $csv_lines = explode( "\n", $csv );
179
+ $csv_head = str_getcsv( 'search,replace' );
180
+ foreach ( $csv_lines as $line ) {
181
+ $this->csv_data[] = array_combine( $csv_head, str_getcsv( $line ) );
182
+ }
183
+ }
184
+
185
  for ( $page = 0; $page < $pages; $page ++ ) {
186
 
187
  $start = $page * $page_size;
190
  $data = $this->dbm->get_table_content( $table, $start, $page_size );
191
 
192
  if ( ! $data ) {
193
+ $table_report['errors'][] = 'no data in table ' . $table;
194
  }
195
 
196
  foreach ( $data as $row ) {
197
+ ++ $table_report['rows'];
198
 
199
+ $update_sql = [];
200
+ $where_sql = [];
201
+ $update = true;
 
 
202
 
203
  foreach ( $columns as $column ) {
204
+ // Skip the GUID column per WordPress Codex.
205
+ if ( $column === 'guid' ) {
206
+ continue;
207
+ }
208
 
209
  $data_to_fix = $row[ $column ];
210
 
212
  $where_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $data_to_fix ) . '"';
213
  continue;
214
  }
 
 
 
 
 
 
 
215
 
216
  // Run a search replace on the data that'll respect the serialisation.
217
+ if ( is_serialized( $data_to_fix, false )
218
+ && in_array( strtolower( $column ), $maybe_serialized, true )
219
+ ) {
220
+ // Run a search replace on the data that'll respect the serialisation.
221
+ $edited_data = $this->recursive_unserialize_replace( $search, $replace, $data_to_fix );
222
+ } else {
223
+ $edited_data = str_replace( $search, $replace, $data_to_fix );
224
+ }
225
 
226
+ // Run a search replace by CSV parameters if CSV input present
227
+ if ( null !== $csv ) {
228
+ foreach ( $this->csv_data as $entry ) {
229
+ $edited_data = is_serialized( $edited_data, false ) ?
230
+ $this->recursive_unserialize_replace(
231
+ $entry['search'],
232
+ $entry['replace'],
233
+ $edited_data
234
+ ) : str_replace( $entry['search'], $entry['replace'], $data_to_fix );
235
+ }
236
+ }
237
 
238
+ // Something was changed.
239
+ if ( $edited_data !== $data_to_fix ) {
240
+ ++ $table_report['change'];
241
 
242
  // log changes
243
+ // @todo : does it work with non UTF-8 encodings?
244
+ $table_report['changes'][] = [
245
+ 'row' => $table_report['rows'],
246
  'column' => $column,
247
  'from' => $data_to_fix,
248
  'to' => $edited_data,
249
+ ];
250
 
251
  $update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $edited_data ) . '"';
252
+ $update = true;
 
253
  }
 
254
  }
255
 
256
  // Determine what to do with updates.
257
+ if ( true === $this->dry_run ) {
258
+ // Don't do anything if a dry run.
259
+ continue;
260
+ }
261
 
262
+ if ( $update && ! empty( $where_sql ) ) {
263
+ // If there are changes to make, run the query.
264
  $result = $this->dbm->update( $table, $update_sql, $where_sql );
265
 
266
  if ( ! $result ) {
267
+ $table_report['errors'][] = sprintf(
268
+ /* translators: $1 is the number of rows found in database */
269
+ esc_html__( 'Error updating row: %d.', 'search-and-replace' ),
270
  $row
271
  );
272
  } else {
273
+ $table_report['updates'] ++;
274
  }
 
275
  }
276
  }
277
  }
278
 
279
+ $table_report['end'] = microtime( true );
280
+
281
  $this->dbm->flush();
282
 
283
  return $table_report;
285
  }
286
 
287
  /**
288
+ * Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
289
+ *
290
+ * @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
291
+ * @access public
292
+ *
293
+ * @param array|string $input The string to escape.
294
+ *
295
+ * @return string
296
+ */
297
+ public function mysql_escape_mimic( $input ) {
298
+
299
+ if ( is_array( $input ) ) {
300
+ return array_map( __METHOD__, $input );
301
+ }
302
+ if ( ! empty( $input ) && is_string( $input ) ) {
303
+ return str_replace(
304
+ [ '\\', "\0", "\n", "\r", "'", '"', "\x1a" ],
305
+ [ '\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z' ],
306
+ $input
307
+ );
308
+ }
309
+
310
+ return $input;
311
+ }
312
+
313
+ /**
314
+ * Recursive unserialize replace
315
+ *
316
  * Take a serialised array and unserialize it replacing elements as needed and
317
  * unserializing any subordinate arrays and performing the replace on those too.
318
  *
319
+ * Be aware, the method due to his recursive characteristic cannot prevent execution in case the
320
+ * data passed to the function isn't a serialized value at the first time.
321
+ *
322
+ * It's up to you to be sure the value is serialized before call the method.
323
+ *
324
+ * @param string $from String we're looking to replace.
325
+ * @param string $to What we want it to be replaced with.
326
+ * @param array|string|object $data Used to pass any subordinate arrays back to in.
327
+ * @param bool $serialised Does the array passed via $data need serialising. Default to true.
328
+ *
329
+ * @throws \Throwable Whatever exception is thrown if WP_DEBUG is true.
330
  *
331
  * @return array The original array with all elements replaced as needed.
332
  */
333
+ public function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialised = true ) {
334
 
335
+ // Some unserialized data cannot be re-serialised eg. SimpleXMLElements.
336
  try {
337
+ $unserialized = is_serialized( $data, false ) ? maybe_unserialize( $data ) : false;
338
 
339
+ if ( $unserialized !== false && ! is_serialized_string( $data ) ) {
340
+ $data = $this->recursive_unserialize_replace( $from, $to, $unserialized, false );
341
  } elseif ( is_array( $data ) ) {
342
+ $_tmp = [];
343
+ foreach ( (array) $data as $key => $value ) {
344
+ $_tmp[ $key ] = $this->recursive_unserialize_replace( $from, $to, $value, false );
345
  }
346
 
347
  $data = $_tmp;
348
+
349
  unset( $_tmp );
350
+ } elseif ( is_object( $data ) ) {
351
+ $_tmp = $data;
 
 
352
  $props = get_object_vars( $data );
353
  foreach ( $props as $key => $value ) {
354
+ $_tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false );
355
  }
356
 
357
  $data = $_tmp;
358
+
359
  unset( $_tmp );
360
  } else {
361
+ // Don't process data that isn't a string.
362
+ // In this case, just return it because we haven't coverage for this kind of value.
363
+ if ( ! is_string( $data ) ) {
364
+ return $data;
365
+ }
366
+
367
+ $marker = false;
368
+
369
+ if ( is_serialized_string( $data ) ) {
370
+ // @codingStandardsIgnoreLine
371
+ $data = maybe_unserialize( $data );
372
+ $marker = true;
373
+ }
374
+
375
+ $tmp_data = $data;
376
+ $data = str_replace( $from, $to, $data );
377
 
378
+ // Do not allow to return valid serialized data,
379
+ // If after replacement data is_serialized then add one | to the replacement.
380
+ if ( is_serialized( $data, false ) ) {
381
+ $data = str_replace( $from, '|' . $to, $tmp_data );
382
+ }
383
+
384
+ if ( $marker ) {
385
+ $data = maybe_serialize( $data );
386
  }
387
  }
388
 
389
  if ( $serialised ) {
390
+ // @codingStandardsIgnoreLine
391
+ $data = serialize( $data );
392
+ }
393
+ } catch ( \Throwable $throwable ) {
394
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
395
+ throw $throwable;
396
  }
397
 
398
+ do_action( 'inpsyde.search_and_replace.error', $throwable );
 
 
 
 
399
  }
400
 
401
  return $data;
402
  }
403
 
404
  /**
405
+ * Returns true, if dry run, false if not
 
 
 
406
  *
407
  * @return bool
408
  */
409
+ public function get_dry_run() {
410
 
411
+ return $this->dry_run;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  }
413
 
414
  /**
415
  * Sets the dry run option.
416
  *
417
+ * @param bool $state : TRUE for dry run, FALSE for writing changes to DB.
418
  *
419
  * @return bool
420
  */
427
 
428
  return $state;
429
  }
430
+ }
 
 
 
 
 
 
 
 
 
 
 
inc/FileDownloader.php CHANGED
@@ -1,7 +1,9 @@
1
  <?php
 
2
  namespace Inpsyde\SearchReplace;
3
 
4
  use Inpsyde\SearchReplace\Database\Exporter;
 
5
 
6
  /**
7
  * Class FileDownloader
@@ -18,21 +20,28 @@ class FileDownloader {
18
  /**
19
  * @var string
20
  */
21
- private $nonce_name = 'insr_nonce';
22
 
23
  /**
24
  * @var Exporter
25
  */
26
- protected $dbe;
 
 
 
 
 
27
 
28
  /**
29
  * Admin constructor.
30
  *
31
  * @param Exporter $dbe
 
32
  */
33
- public function __construct( Exporter $dbe ) {
34
 
35
- $this->dbe = $dbe;
 
36
  }
37
 
38
  /**
@@ -42,93 +51,78 @@ class FileDownloader {
42
  */
43
  public function show_modal( $report ) {
44
 
45
- if ( array_key_exists( 'changes', $report ) && ! empty( $report[ 'changes' ] ) ) {
 
 
 
 
46
  ?>
47
  <div class="updated notice is-dismissible">
48
- <?php
49
- //show changes if there are any
50
- if ( count( $report[ 'changes' ] ) > 0 ) {
51
- $this->show_changes( $report );
52
- }
53
 
54
- //if no changes found report that
55
- if ( 0 === count( $report [ 'changes' ] ) ) {
56
- echo '<p>' . esc_html__( 'Search pattern not found.', 'search-and-replace' ) . '</p>';
57
- }
58
- ?>
59
  </div>
60
- <?php
61
- }
62
-
63
- $compress = (bool) ( isset( $_POST[ 'compress' ] ) && 'on' === $_POST[ 'compress' ] );
64
-
65
  ?>
66
 
67
-
68
-
69
  <div class="updated notice is-dismissible insr_sql_button_wrap">
70
  <p><?php esc_html_e( 'Your SQL file was created!', 'search-and-replace' ); ?> </p>
71
  <form action method="post">
72
  <?php wp_nonce_field( $this->nonce_action, $this->nonce_name ); ?>
73
- <input type="hidden" name="action" value="download_file" />
74
- <input type="hidden" name="sql_file" value="<?php echo esc_attr( $report[ 'filename' ] ); ?>">
75
  <input type="hidden" name="compress" value="<?php echo esc_attr( $compress ); ?>">
76
  <input id="insr_submit" type="submit" value="<?php esc_attr_e(
77
  'Download SQL File', 'search-and-replace'
78
- ) ?>" class="button" />
79
  </form>
80
  </div>
81
  <?php
82
  }
83
 
84
  /**
85
- * displays the changes made to the db
86
- * echoes the changes in formatted html
87
  *
88
- * @param $report array 'errors' : WP-Error Object if Errors
89
- * 'tables' : Number of tables processed
90
- * 'changes_count' : Number of changes made
91
- * 'changes'
92
- * Array with at least these elements:
93
- * 'table_name'=>$[name of current table],
94
- * 'changes' => array('row' => [row that has been changed ],
95
- * 'column' => [column that has been changed],
96
- * 'from' => ( old value ),
97
- * 'to' => ( $new value ),
98
  *
99
- * @return string
 
 
 
 
 
 
 
 
100
  */
101
  public function show_changes( $report ) {
102
 
103
- //get search & replace values in order to highlight them in the results
104
- $search = esc_html( $_POST [ 'search' ] );
 
 
 
 
 
 
 
 
105
  $search_highlight = '<span class="search-replace-search-value">' . $search . '</span>';
106
- $replace = esc_html( $_POST [ 'replace' ] );
107
  $replace_highlight = '<span class ="search-replace-replace-value">' . $replace . '</span>';
108
- $delimiter = array( ' ...', '...<br>' );
109
-
110
- $msg = sprintf(
111
- _n(
112
- '%s table was processed. ',
113
- '%s tables were processed. ',
114
- $report[ 'tables' ],
115
- 'search-and-replace'
116
- ),
117
- $report[ 'tables' ]
118
- );
119
 
120
- $msg .= sprintf(
121
- _n(
122
- '%s cell needs to be updated. ',
123
- '%s cells need to be updated.',
124
- $report[ 'changes_count' ],
125
- 'search-and-replace'
126
- ),
127
- $report[ 'changes_count' ]
128
- );
129
- echo esc_html( $msg );
130
 
131
- //create modal window for detailed view of changes
132
  ?>
133
  <p><a href="#" id="changes-modal-button"><?php esc_html_e( 'View details', 'search-and-replace' ); ?></a></p>
134
  <div id="changes-modal-background" class="search-replace-modal-background" style="display: none;"></div>
@@ -138,51 +132,76 @@ class FileDownloader {
138
  </div>
139
  <div class="search-replace-changes-modal-content">
140
  <?php
141
- foreach ( $report[ 'changes' ] as $table_report ) :
142
- $changes = $table_report[ 'changes' ];
143
  $changes_made = count( $changes );
144
 
145
- if ( $changes_made < 1 ) :
146
  continue;
147
- endif;
148
 
149
- $table = $table_report[ 'table_name' ];
150
  ?>
151
  <h2 class="search-replace-modal-table-headline">
152
  <strong><?php esc_html_e( 'Table:', 'search-and-replace' ); ?></strong>
153
- <?php echo $table; ?>
154
- <strong><?php esc_html_e( 'Changes:', 'search-and-replace' ); ?></strong>
155
- <?php echo $changes_made; ?>
 
156
  </h2>
157
 
158
  <table class="search-replace-modal-table">
159
 
160
- <?php foreach ( $changes as $change ) : ?>
161
-
162
- <tr>
163
- <th class="search-replace-narrow">
164
- <?php esc_html_e( 'row', 'search-and-replace' ); ?>
165
- </th>
166
- <td class="search-replace-narrow"><?php echo esc_html( $change [ 'row' ] ); ?></td>
167
- <th><?php esc_html_e( 'column', 'search-and-replace' ); ?></th>
168
- <td><?php echo esc_html( $change [ 'column' ] ); ?></td>
169
- <?php
170
- //trim results and wrap with highlight class
171
- $old_value = esc_html( $change [ 'from' ] );
172
- $old_value = $this->trim_search_results( $search, $old_value, $delimiter );
173
- $old_value = str_replace( $search, $search_highlight, $old_value );
174
-
175
- $new_value = esc_html( $change[ 'to' ] );
176
- $new_value = $this->trim_search_results( $replace, $new_value, $delimiter );
177
- $new_value = str_replace( $replace, $replace_highlight, $new_value );
 
 
 
 
 
 
 
 
 
 
 
 
178
  ?>
179
- <th><?php esc_html_e( 'Old value:', 'search-and-replace' ); ?></th>
180
- <td><?php echo wp_kses( $old_value, [ 'span' => [ 'class' => [] ] ] ); ?></td>
181
- <th><?php esc_html_e( 'New value:', 'search-and-replace' ); ?></th>
182
- <td><?php echo wp_kses( $new_value, [ 'span' => [ 'class' => [] ] ] ); ?></td>
183
- </tr>
184
-
185
- <?php endforeach ?>
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
  </table>
188
 
@@ -193,85 +212,163 @@ class FileDownloader {
193
  <?php
194
  }
195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  /**
197
  * Trims a given string to 50 chars before and after the search string, if the string is longer than 199 chars.
198
  *
199
- * @param $needle string
200
- * @param $haystack string
201
- * @param $delimiter array $delimiter[0]=start delimiter, $delimiter[1] = end delimiter
202
  *
203
  * @return string The trimmed $haystack
204
  */
205
  public function trim_search_results( $needle, $haystack, $delimiter ) {
206
 
207
- //if result has <200 characters we return the whole string
208
  if ( strlen( $haystack ) < 100 ) {
209
  return $haystack;
210
  }
211
- $trimmed_results = NULL;
212
  // Get all occurrences of $needle with up to 50 chars front & back.
213
- preg_match_all( '@.{0,50}' . $needle . '.{0,50}@', $haystack, $trimmed_results );
214
  $return_value = '';
215
- /** @var array $trimmed_results */
216
- $imax = count( $trimmed_results );
217
- for ( $i = 0; $i < $imax; $i ++ ) {
218
- //reset delimiter, might have been changed
 
 
 
 
219
  $local_delimiter = $delimiter;
220
- //check if the first trimmmed result is the beginning of $haystack. if so remove leading delimiter
 
221
  if ( $i === 0 ) {
222
- $pos = strpos( $haystack, $trimmed_results[ 0 ][ $i ] );
223
  if ( $pos === 0 ) {
224
- $local_delimiter[ 0 ] = '';
225
  }
226
  }
227
- //check if the last trimmed result is the end of $haystack. if so, remove trailing delimiter
228
- $last_index = count( $trimmed_results ) - 1;
 
 
229
  if ( $i === $last_index ) {
230
- $trimmed_result_length = strlen( $trimmed_results[ 0 ][ $i ] );
231
  $substring = substr( $haystack, - $trimmed_result_length );
232
- if ( $substring === $trimmed_results[ 0 ][ $i ] ) {
233
- $local_delimiter[ 1 ] = '';
 
234
  }
235
  }
236
- $return_value .= $local_delimiter[ 0 ] . $trimmed_results[ 0 ][ $i ] . $local_delimiter[ 1 ];
237
  }
238
 
239
  return $return_value;
240
  }
 
241
  /**
242
- * calls the file delivery in Class DatabaseExporter
243
  *
244
  * @wp-hook init
 
 
245
  */
246
  public function deliver_backup_file() {
247
 
248
- if ( ! $_SERVER[ 'REQUEST_METHOD' ] === 'POST' ) {
249
- return;
 
 
 
 
 
 
 
 
 
250
  }
251
 
252
- if ( ! isset( $_POST[ 'insr_nonce' ] ) || ! wp_verify_nonce( $_POST[ 'insr_nonce' ], 'download_sql' ) ) {
253
- return;
 
 
254
  }
255
 
256
- if ( isset( $_POST[ 'action' ] ) && 'download_file' === $_POST[ 'action' ] ) {
257
 
258
- $sql_file = '';
259
- if ( isset( $_POST[ 'sql_file' ] ) ) {
260
- $sql_file = $_POST[ 'sql_file' ];
261
- }
262
 
263
- $compress = FALSE;
264
- if ( isset( $_POST[ 'compress' ] ) ) {
265
- $compress = $_POST[ 'compress' ];
266
- }
267
 
268
- // If file name contains path or does not end with '.sql' exit.
269
- $ext = strrchr( $sql_file, '.' );
270
- if ( FALSE !== strpos( $sql_file, '/' ) || '.sql' !== $ext ) {
271
- die;
272
- }
273
- $this->dbe->deliver_backup( $sql_file, $compress );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  }
275
 
 
 
 
 
 
 
276
  }
277
- }
1
  <?php
2
+
3
  namespace Inpsyde\SearchReplace;
4
 
5
  use Inpsyde\SearchReplace\Database\Exporter;
6
+ use Inpsyde\SearchReplace\Service\MaxExecutionTime;
7
 
8
  /**
9
  * Class FileDownloader
20
  /**
21
  * @var string
22
  */
23
+ private $nonce_name = 'insr_nonce_download';
24
 
25
  /**
26
  * @var Exporter
27
  */
28
+ private $dbe;
29
+
30
+ /**
31
+ * @var MaxExecutionTime
32
+ */
33
+ private $max_execution;
34
 
35
  /**
36
  * Admin constructor.
37
  *
38
  * @param Exporter $dbe
39
+ * @param MaxExecutionTime $max_execution
40
  */
41
+ public function __construct( Exporter $dbe, MaxExecutionTime $max_execution ) {
42
 
43
+ $this->dbe = $dbe;
44
+ $this->max_execution = $max_execution;
45
  }
46
 
47
  /**
51
  */
52
  public function show_modal( $report ) {
53
 
54
+ // Set compress status.
55
+ // @codingStandardsIgnoreLine
56
+ $compress = ( isset( $_POST['compress'] ) && 'on' === $_POST['compress'] );
57
+
58
+ if ( array_key_exists( 'changes', $report ) && ! empty( $report['changes'] ) ) :
59
  ?>
60
  <div class="updated notice is-dismissible">
61
+ <?php
62
+ // Show changes if there are any.
63
+ if ( count( $report['changes'] ) > 0 ) {
64
+ $this->show_changes( $report );
65
+ }
66
 
67
+ // If no changes found report that.
68
+ if ( 0 === count( $report ['changes'] ) ) {
69
+ echo '<p>' . esc_html__( 'Search pattern not found.', 'search-and-replace' ) . '</p>';
70
+ }
71
+ ?>
72
  </div>
73
+ <?php
74
+ endif;
 
 
 
75
  ?>
76
 
 
 
77
  <div class="updated notice is-dismissible insr_sql_button_wrap">
78
  <p><?php esc_html_e( 'Your SQL file was created!', 'search-and-replace' ); ?> </p>
79
  <form action method="post">
80
  <?php wp_nonce_field( $this->nonce_action, $this->nonce_name ); ?>
81
+ <input type="hidden" name="action" value="download_file"/>
82
+ <input type="hidden" name="sql_file" value="<?php echo esc_attr( $report['filename'] ); ?>">
83
  <input type="hidden" name="compress" value="<?php echo esc_attr( $compress ); ?>">
84
  <input id="insr_submit" type="submit" value="<?php esc_attr_e(
85
  'Download SQL File', 'search-and-replace'
86
+ ) ?>" class="button"/>
87
  </form>
88
  </div>
89
  <?php
90
  }
91
 
92
  /**
93
+ * Displays the changes made to the db
94
+ * echoes the changes in formatted html.
95
  *
96
+ * @param array $report WP-Error Object if Errors
 
 
 
 
 
 
 
 
 
97
  *
98
+ * 'tables' : Number of tables processed
99
+ * 'changes_count' : Number of changes made
100
+ * 'changes' : Array with at least these elements:
101
+ * 'table_name'=> $[name of current table],
102
+ * 'changes' => array('row' => [row that has been changed ],
103
+ * 'column' => [column that has been changed],
104
+ * 'from' => ( old value ),
105
+ * 'to' => ( $new value ),
106
+ * .
107
  */
108
  public function show_changes( $report ) {
109
 
110
+ // Get search & replace values in order to highlight them in the results.
111
+ // @codingStandardsIgnoreStart
112
+ $search = isset( $_POST['search'] ) ?
113
+ esc_html( filter_var( $_POST ['search'], FILTER_SANITIZE_STRING ) ) :
114
+ '';
115
+ $replace = isset( $_POST['replace'] ) ?
116
+ esc_html( filter_var( $_POST ['replace'], FILTER_SANITIZE_STRING ) ) :
117
+ '';
118
+ // @codingStandardsIgnoreEnd
119
+
120
  $search_highlight = '<span class="search-replace-search-value">' . $search . '</span>';
 
121
  $replace_highlight = '<span class ="search-replace-replace-value">' . $replace . '</span>';
122
+ $delimiter = [ ' ...', '...<br>' ];
 
 
 
 
 
 
 
 
 
 
123
 
124
+ $this->show_message_by_report( $report );
 
 
 
 
 
 
 
 
 
125
 
 
126
  ?>
127
  <p><a href="#" id="changes-modal-button"><?php esc_html_e( 'View details', 'search-and-replace' ); ?></a></p>
128
  <div id="changes-modal-background" class="search-replace-modal-background" style="display: none;"></div>
132
  </div>
133
  <div class="search-replace-changes-modal-content">
134
  <?php
135
+ foreach ( $report['changes'] as $table_report ) :
136
+ $changes = $table_report['changes'];
137
  $changes_made = count( $changes );
138
 
139
+ if ( $changes_made < 1 ) {
140
  continue;
141
+ }
142
 
143
+ $table = $table_report['table_name'];
144
  ?>
145
  <h2 class="search-replace-modal-table-headline">
146
  <strong><?php esc_html_e( 'Table:', 'search-and-replace' ); ?></strong>
147
+ <?php echo esc_html( $table ); ?>
148
+
149
+ <strong><?php esc_html_e( 'Columns Changes:', 'search-and-replace' ); ?></strong>
150
+ <?php echo esc_html( $changes_made ); ?>
151
  </h2>
152
 
153
  <table class="search-replace-modal-table">
154
 
155
+ <thead>
156
+ <tr>
157
+ <th class="search-replace-row search-replace-narrow">
158
+ <?php esc_html_e( 'Row', 'search-and-replace' ); ?>
159
+ </th>
160
+ <th class="search-replace-column">
161
+ <?php esc_html_e( 'Column', 'search-and-replace' ); ?>
162
+ </th>
163
+ <th class="search-replace-old-value">
164
+ <?php esc_html_e( 'Old value', 'search-and-replace' ); ?>
165
+ </th>
166
+ <th class="search-replace-new-value">
167
+ <?php esc_html_e( 'New value', 'search-and-replace' ); ?>
168
+ </th>
169
+ </tr>
170
+ </thead>
171
+
172
+ <tbody>
173
+ <?php
174
+ foreach ( $changes as $change ) :
175
+ // Trim results and wrap with highlight class.
176
+ $old_value = esc_html( $change ['from'] );
177
+ $old_value = $this->trim_search_results( $search, $old_value, $delimiter );
178
+ $old_value = str_replace( $search, $search_highlight, $old_value );
179
+
180
+ $new_value = esc_html( $change['to'] );
181
+ $new_value = $this->trim_search_results( $replace, $new_value, $delimiter );
182
+ $new_value = str_replace( $replace, $replace_highlight, $new_value );
183
+
184
+ if ( $old_value && $new_value ) :
185
  ?>
186
+ <tr>
187
+ <td class="search-replace-row search-replace-narrow">
188
+ <?php echo esc_html( $change ['row'] ); ?>
189
+ </td>
190
+ <td class="search-replace-column">
191
+ <?php echo esc_html( $change ['column'] ); ?>
192
+ </td>
193
+ <td class="search-replace-old-value">
194
+ <?php echo wp_kses( $old_value, [ 'span' => [ 'class' => [] ] ] ); ?>
195
+ </td>
196
+ <td class="search-replace-new-value">
197
+ <?php echo wp_kses( $new_value, [ 'span' => [ 'class' => [] ] ] ); ?>
198
+ </td>
199
+ </tr>
200
+ <?php
201
+ endif;
202
+ endforeach;
203
+ ?>
204
+ </tbody>
205
 
206
  </table>
207
 
212
  <?php
213
  }
214
 
215
+ /**
216
+ * Show Message by Report
217
+ *
218
+ * @param array $report The list of all the changes made.
219
+ *
220
+ * @return void
221
+ */
222
+ private function show_message_by_report( $report ) {
223
+
224
+ $msg = sprintf(
225
+ // translators: %s is the count of tables.
226
+ _n(
227
+ '%s table was processed. ',
228
+ '%s tables were processed. ',
229
+ $report['tables'],
230
+ 'search-and-replace'
231
+ ),
232
+ (int) $report['tables']
233
+ );
234
+
235
+ $msg .= sprintf(
236
+ // translators: %s is the count of selected cells after search in tables.
237
+ _n(
238
+ '%s cell needs to be updated. ',
239
+ '%s cells need to be updated.',
240
+ $report['changes_count'],
241
+ 'search-and-replace'
242
+ ),
243
+ (int) $report['changes_count']
244
+ );
245
+
246
+ echo '<p>' . esc_html( $msg ) . '</p>';
247
+ }
248
+
249
  /**
250
  * Trims a given string to 50 chars before and after the search string, if the string is longer than 199 chars.
251
  *
252
+ * @param string $needle
253
+ * @param string $haystack
254
+ * @param array $delimiter $delimiter[0]=start delimiter, $delimiter[1] = end delimiter.
255
  *
256
  * @return string The trimmed $haystack
257
  */
258
  public function trim_search_results( $needle, $haystack, $delimiter ) {
259
 
260
+ // Ff result has <100 characters we return the whole string.
261
  if ( strlen( $haystack ) < 100 ) {
262
  return $haystack;
263
  }
264
+ $trimmed_results = null;
265
  // Get all occurrences of $needle with up to 50 chars front & back.
266
+ $matches = preg_match_all( '@.{0,50}' . $needle . '.{0,50}@', $haystack, $trimmed_results );
267
  $return_value = '';
268
+
269
+ // Don't need to perform any action if no matches.
270
+ if ( ! $matches ) {
271
+ return $return_value;
272
+ }
273
+
274
+ for ( $i = 0; $i < $matches; $i ++ ) {
275
+ // Reset delimiter, might have been changed.
276
  $local_delimiter = $delimiter;
277
+
278
+ // Check if the first trimmed result is the beginning of $haystack. if so remove leading delimiter.
279
  if ( $i === 0 ) {
280
+ $pos = strpos( $haystack, $trimmed_results[0][ $i ] );
281
  if ( $pos === 0 ) {
282
+ $local_delimiter[0] = '';
283
  }
284
  }
285
+
286
+ // Check if the last trimmed result is the end of $haystack. if so, remove trailing delimiter.
287
+ $last_index = $matches - 1;
288
+
289
  if ( $i === $last_index ) {
290
+ $trimmed_result_length = strlen( $trimmed_results[0][ $i ] );
291
  $substring = substr( $haystack, - $trimmed_result_length );
292
+
293
+ if ( $substring === $trimmed_results[0][ $i ] ) {
294
+ $local_delimiter[1] = '';
295
  }
296
  }
297
+ $return_value .= $local_delimiter[0] . $trimmed_results[0][ $i ] . $local_delimiter[1];
298
  }
299
 
300
  return $return_value;
301
  }
302
+
303
  /**
304
+ * Calls the file delivery in Class DatabaseExporter.
305
  *
306
  * @wp-hook init
307
+ *
308
+ * @return bool
309
  */
310
  public function deliver_backup_file() {
311
 
312
+ // Retrieve the nonce value.
313
+ // @codingStandardsIgnoreStart
314
+ $nonce = isset( $_POST[ $this->nonce_name ] ) ?
315
+ filter_var( $_POST[ $this->nonce_name ], FILTER_SANITIZE_STRING )
316
+ : '';
317
+ // @codingStandardsIgnoreEnd
318
+
319
+ // If nonce has not been send, just return nothing else to do here.
320
+ // The method may be hooked to a wp action, so it's executed on every page request.
321
+ if ( ! $nonce ) {
322
+ return false;
323
  }
324
 
325
+ // Die in case the nonce has been passed but not a valid one.
326
+ // @codingStandardsIgnoreLine
327
+ if ( ! wp_verify_nonce( $_POST[ $this->nonce_name ], $this->nonce_action ) ) {
328
+ wp_die( 'Cheating Uh?' );
329
  }
330
 
331
+ $this->max_execution->set();
332
 
333
+ // Get the action to perform.
334
+ // @codingStandardsIgnoreLine
335
+ $action = isset( $_POST['action'] ) ? filter_var( $_POST['action'], FILTER_SANITIZE_STRING ) : '';
 
336
 
337
+ if ( 'download_file' !== $action ) {
338
+ return false;
339
+ }
 
340
 
341
+ $sql_file = '';
342
+ $compress = false;
343
+
344
+ // @codingStandardsIgnoreLine
345
+ if ( isset( $_POST['sql_file'] ) ) {
346
+ // @codingStandardsIgnoreLine
347
+ $sql_file = sanitize_file_name( $_POST['sql_file'] );
348
+ }
349
+
350
+ if ( ! $sql_file ) {
351
+ wp_die( esc_html__( 'The file you are looking for doesn\'t exists.', 'search-and-replace' ) );
352
+ }
353
+
354
+ // If file name contains path or does not end with '.sql' exit.
355
+ // @ToDo create a function to prevent traversal path.
356
+ $ext = strrchr( $sql_file, '.' );
357
+ if ( false !== strpos( $sql_file, '/' ) || '.sql' !== $ext ) {
358
+ wp_die( 'Cheating Uh?' );
359
+ }
360
+
361
+ // @codingStandardsIgnoreLine
362
+ if ( isset( $_POST['compress'] ) ) {
363
+ // @codingStandardsIgnoreLine
364
+ $compress = (bool) filter_var( $_POST['compress'], FILTER_VALIDATE_BOOLEAN );
365
  }
366
 
367
+ // Download the file.
368
+ $this->dbe->deliver_backup( $sql_file, $compress );
369
+
370
+ $this->max_execution->restore();
371
+
372
+ return true;
373
  }
374
+ }
inc/Load.php DELETED
@@ -1,72 +0,0 @@
1
- <?php
2
- namespace Inpsyde\SearchReplace;
3
-
4
- use Requisite\Requisite;
5
- use Requisite\Rule\Psr4;
6
- use Requisite\SPLAutoLoader;
7
-
8
- /**
9
- * Class Load handle plugin loading
10
- *
11
- * @since 3.1.1
12
- *
13
- * @package Inpsyde\SearchReplace
14
- */
15
- class Load {
16
-
17
- /**
18
- * Initialize the plugin autoload files
19
- *
20
- * @since 3.1.1
21
- *
22
- * @return void
23
- */
24
- public function init(){
25
-
26
- search_replace_textdomain();
27
-
28
- if( $this->user_can_access() === TRUE ) {
29
-
30
- /**
31
- * Load the Requisite library. Alternatively you can use composer's
32
- */
33
- require_once __DIR__ . '/requisite/src/Requisite/Requisite.php';
34
- Requisite::init();
35
-
36
- $autoloader = new SPLAutoLoader;
37
-
38
- $autoloader->addRule(
39
- new Psr4(
40
- __DIR__, // base directory
41
- 'Inpsyde\SearchReplace' // base namespace
42
- )
43
- );
44
-
45
- $plugin = new Plugin();
46
- $plugin->run();
47
- }
48
-
49
- }
50
-
51
- /**
52
- * Validate user access
53
- * To change the user access capability use the filter search_replace_access_capability
54
- * @see https://codex.wordpress.org/Roles_and_Capabilities
55
- *
56
- * @since 3.1.1
57
- *
58
- * @return bool
59
- */
60
- private function user_can_access(){
61
-
62
- $user_cap = apply_filters( 'search_replace_access_capability', 'manage_options' );
63
-
64
- if ( current_user_can( $user_cap ) ) {
65
- return true;
66
- }
67
-
68
- return false;
69
-
70
- }
71
-
72
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/Page/AbstractPage.php CHANGED
@@ -18,7 +18,7 @@ abstract class AbstractPage {
18
 
19
  /**
20
  * By default "Search & Replace". Can be overwritten in child-classes.
21
- *
22
  * @return string
23
  */
24
  public function get_menu_title() {
@@ -48,25 +48,35 @@ abstract class AbstractPage {
48
  return;
49
  }
50
 
51
- $html = '<div class="error notice is-dismissible">';
52
- $html .= sprintf( '<strong>%s</strong>', esc_html__( 'Errors:', 'search-and-replace' ) );
53
- $html .= '<ul>';
54
- foreach ( $this->errors as $error ) :
55
- $html .= '<li>' . esc_html( $error ) . '</li>';
56
- endforeach;
57
- $html .= '</ul>';
58
- $html .= '</div>';
59
-
60
- echo $html;
 
 
 
 
 
61
  }
62
 
63
  /**
64
- *displays the html for the submit button
 
 
65
  */
66
- public function show_submit_button() {
67
 
68
- echo '<input type="hidden" name="action" value="' . $this->get_slug() . '" />';
69
- submit_button( $this->get_submit_button_title() );
 
 
 
70
  wp_nonce_field( 'replace_domain', 'insr_nonce' );
71
  }
72
 
@@ -85,4 +95,4 @@ abstract class AbstractPage {
85
 
86
  return sanitize_title_with_dashes( $this->get_page_title() );
87
  }
88
- }
18
 
19
  /**
20
  * By default "Search & Replace". Can be overwritten in child-classes.
21
+ *
22
  * @return string
23
  */
24
  public function get_menu_title() {
48
  return;
49
  }
50
 
51
+ ?>
52
+ <div class="error notice is-dismissible">
53
+ <p>
54
+ <strong>
55
+ <?php esc_html_e( 'Errors:', 'search-and-replace' ); ?>
56
+ </strong>
57
+ </p>
58
+ <ul>
59
+ <?php foreach ( $this->errors as $error ) : ?>
60
+ <li><?= esc_html( $error ); ?></li>
61
+ <?php endforeach; ?>
62
+ </ul>
63
+ </div>
64
+
65
+ <?php
66
  }
67
 
68
  /**
69
+ * Displays the html for the submit button.
70
+ *
71
+ * @param string $name
72
  */
73
+ public function show_submit_button( $name = 'submit' ) {
74
 
75
+ printf(
76
+ '<input type="hidden" name="action" value="%s" />',
77
+ esc_attr( $this->get_slug() )
78
+ );
79
+ submit_button( $this->get_submit_button_title(), 'primary', $name );
80
  wp_nonce_field( 'replace_domain', 'insr_nonce' );
81
  }
82
 
95
 
96
  return sanitize_title_with_dashes( $this->get_page_title() );
97
  }
98
+ }
inc/Page/BackupDatabase.php CHANGED
@@ -52,11 +52,11 @@ class BackupDatabase extends AbstractPage implements PageInterface {
52
  }
53
 
54
  /**
55
- *shows the page template
56
  */
57
  public function render() {
58
 
59
- require_once( __DIR__ . '/../templates/db_backup.php' );
60
  }
61
 
62
  /**
@@ -68,7 +68,7 @@ class BackupDatabase extends AbstractPage implements PageInterface {
68
  }
69
 
70
  /**
71
- * event handler for click on export sql button
72
  */
73
  public function save() {
74
 
@@ -78,4 +78,4 @@ class BackupDatabase extends AbstractPage implements PageInterface {
78
  return TRUE;
79
  }
80
 
81
- }
52
  }
53
 
54
  /**
55
+ * Shows the page template
56
  */
57
  public function render() {
58
 
59
+ require_once( __DIR__ . '/../templates/db-backup.php' );
60
  }
61
 
62
  /**
68
  }
69
 
70
  /**
71
+ * Event handler for click on export sql button
72
  */
73
  public function save() {
74
 
78
  return TRUE;
79
  }
80
 
81
+ }
inc/Page/Credits.php CHANGED
@@ -41,4 +41,4 @@ class Credits extends AbstractPage implements PageInterface {
41
 
42
  return TRUE;
43
  }
44
- }
41
 
42
  return TRUE;
43
  }
44
+ }
inc/Page/Manager.php CHANGED
@@ -12,7 +12,7 @@ class Manager {
12
  /**
13
  * @var PageInterface[]
14
  */
15
- private $pages = array();
16
 
17
  /**
18
  * Add page.
@@ -63,7 +63,7 @@ class Manager {
63
  * @param string $cap
64
  * @param PageInterface $page
65
  */
66
- $cap = apply_filters( 'insr-capability', 'install_plugins', $page );
67
 
68
  add_submenu_page(
69
  'tools.php',
@@ -71,7 +71,7 @@ class Manager {
71
  $page->get_menu_title(),
72
  $cap,
73
  $slug,
74
- array( $this, 'render' )
75
  );
76
  }
77
  }
@@ -103,9 +103,9 @@ class Manager {
103
  $output = '<div class="wrap">';
104
  $output .= '<h1 id="title">' . esc_html__( 'Search & Replace', 'search-and-replace' ) . '</h1>';
105
  $output .= '<h2 class="nav-tab-wrapper">';
106
- $page = '';
107
  foreach ( $this->pages as $slug => $page ) :
108
- $class = $current_page === $slug ? 'nav-tab-active' : '';
109
  $output .= sprintf(
110
  '<a class="nav-tab %1$s" href="%2$s">%3$s</a>',
111
  esc_attr( $class ),
@@ -113,16 +113,19 @@ class Manager {
113
  $page->get_page_title()
114
  );
115
  endforeach;
 
 
116
  $output .= '</h2>';
117
 
 
 
 
118
  echo $output;
119
  echo '<div class="tab__content">';
120
  $this->save();
121
  $page->display_errors();
122
- $page = $this->pages[ $current_page ];
123
  $page->render();
124
  echo '</div>';
125
-
126
  echo '</div>'; // wrap
127
  }
128
 
@@ -133,17 +136,16 @@ class Manager {
133
  */
134
  public function register_css() {
135
 
136
- if ( ! isset( $_GET[ 'page' ] ) || ! array_key_exists( $_GET[ 'page' ], $this->pages ) ) {
137
  return;
138
  }
139
 
140
  $suffix = $this->get_script_suffix();
141
-
142
  $url = ( SEARCH_REPLACE_BASEDIR . '/assets/css/inpsyde-search-replace' . $suffix . '.css' );
143
  $handle = 'insr-styles';
144
- wp_register_script( $handle, $url );
145
- wp_enqueue_style( $handle, $url, array(), FALSE, FALSE );
146
 
 
 
147
  }
148
 
149
  /**
@@ -153,17 +155,16 @@ class Manager {
153
  */
154
  public function register_js() {
155
 
156
- if ( ! isset( $_GET[ 'page' ] ) || ! array_key_exists( $_GET[ 'page' ], $this->pages ) ) {
157
  return;
158
  }
159
 
160
  $suffix = $this->get_script_suffix();
161
-
162
  $url = ( SEARCH_REPLACE_BASEDIR . '/assets/js/inpsyde-search-replace' . $suffix . '.js' );
163
  $handle = 'insr-js';
164
- wp_register_script( $handle, $url );
165
- wp_enqueue_script( $handle, $url, array(), FALSE, FALSE );
166
 
 
 
167
  }
168
 
169
  /**
@@ -176,4 +177,17 @@ class Manager {
176
  return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
177
  }
178
 
179
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  /**
13
  * @var PageInterface[]
14
  */
15
+ private $pages = [];
16
 
17
  /**
18
  * Add page.
63
  * @param string $cap
64
  * @param PageInterface $page
65
  */
66
+ $cap = apply_filters( 'insr-capability', 'manage_options', $page );
67
 
68
  add_submenu_page(
69
  'tools.php',
71
  $page->get_menu_title(),
72
  $cap,
73
  $slug,
74
+ [ $this, 'render' ]
75
  );
76
  }
77
  }
103
  $output = '<div class="wrap">';
104
  $output .= '<h1 id="title">' . esc_html__( 'Search & Replace', 'search-and-replace' ) . '</h1>';
105
  $output .= '<h2 class="nav-tab-wrapper">';
106
+
107
  foreach ( $this->pages as $slug => $page ) :
108
+ $class = $current_page === $slug ? 'nav-tab-active' : '';
109
  $output .= sprintf(
110
  '<a class="nav-tab %1$s" href="%2$s">%3$s</a>',
111
  esc_attr( $class ),
113
  $page->get_page_title()
114
  );
115
  endforeach;
116
+ unset( $page );
117
+
118
  $output .= '</h2>';
119
 
120
+ // Set the current page.
121
+ $page = $this->pages[ $current_page ];
122
+
123
  echo $output;
124
  echo '<div class="tab__content">';
125
  $this->save();
126
  $page->display_errors();
 
127
  $page->render();
128
  echo '</div>';
 
129
  echo '</div>'; // wrap
130
  }
131
 
136
  */
137
  public function register_css() {
138
 
139
+ if ( ! $this->is_search_and_replace_admin_page() ) {
140
  return;
141
  }
142
 
143
  $suffix = $this->get_script_suffix();
 
144
  $url = ( SEARCH_REPLACE_BASEDIR . '/assets/css/inpsyde-search-replace' . $suffix . '.css' );
145
  $handle = 'insr-styles';
 
 
146
 
147
+ wp_register_script( $handle, $url );
148
+ wp_enqueue_style( $handle, $url, [], false, false );
149
  }
150
 
151
  /**
155
  */
156
  public function register_js() {
157
 
158
+ if ( ! $this->is_search_and_replace_admin_page() ) {
159
  return;
160
  }
161
 
162
  $suffix = $this->get_script_suffix();
 
163
  $url = ( SEARCH_REPLACE_BASEDIR . '/assets/js/inpsyde-search-replace' . $suffix . '.js' );
164
  $handle = 'insr-js';
 
 
165
 
166
+ wp_register_script( $handle, $url );
167
+ wp_enqueue_script( $handle, $url, [], false, true );
168
  }
169
 
170
  /**
177
  return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
178
  }
179
 
180
+ /**
181
+ * Is admin search and replace page
182
+ *
183
+ * Check against the current screen in admin,
184
+ *
185
+ * @return bool True if current screen is one of the search and replace pages
186
+ */
187
+ private function is_search_and_replace_admin_page() {
188
+
189
+ $current = str_replace( 'tools_page_', '', get_current_screen()->id );
190
+
191
+ return array_key_exists( $current, $this->pages );
192
+ }
193
+ }
inc/Page/PageInterface.php CHANGED
@@ -41,14 +41,14 @@ interface PageInterface {
41
  public function get_slug();
42
 
43
  /**
44
- * rendering the page content.
45
  */
46
  public function render();
47
 
48
  /**
49
- * saving the data.
50
  *
51
  * @return boolean
52
  */
53
  public function save();
54
- }
41
  public function get_slug();
42
 
43
  /**
44
+ * Rendering the page content.
45
  */
46
  public function render();
47
 
48
  /**
49
+ * Saving the data.
50
  *
51
  * @return boolean
52
  */
53
  public function save();
54
+ }
inc/Page/ReplaceDomain.php CHANGED
@@ -43,6 +43,7 @@ class ReplaceDomain extends AbstractPage implements PageInterface {
43
 
44
  /**
45
  * @return bool
 
46
  */
47
  public function save() {
48
 
@@ -50,25 +51,37 @@ class ReplaceDomain extends AbstractPage implements PageInterface {
50
  $replace = esc_url_raw( filter_input( INPUT_POST, 'replace' ) );
51
  $new_db_prefix = esc_attr( filter_input( INPUT_POST, 'new_db_prefix' ) );
52
 
53
- //search field should not be empty
54
  if ( '' === $replace ) {
55
  $this->add_error( esc_html__( 'Replace Field should not be empty.', 'search-and-replace' ) );
56
 
57
- return FALSE;
58
  }
59
 
60
- $report = $this->dbe->db_backup( $search, $replace, array(), TRUE, $new_db_prefix );
 
 
 
 
 
 
 
 
 
 
 
 
61
  $this->downloader->show_modal( $report );
62
 
63
- return TRUE;
64
  }
65
 
66
  /**
67
- * shows the page template
68
  */
69
  public function render() {
70
 
71
- require_once( __DIR__ . '/../templates/replace_domain.php' );
72
  }
73
 
74
  /**
@@ -96,4 +109,4 @@ class ReplaceDomain extends AbstractPage implements PageInterface {
96
 
97
  return 'replace-domain-url';
98
  }
99
- }
43
 
44
  /**
45
  * @return bool
46
+ * @throws \Throwable
47
  */
48
  public function save() {
49
 
51
  $replace = esc_url_raw( filter_input( INPUT_POST, 'replace' ) );
52
  $new_db_prefix = esc_attr( filter_input( INPUT_POST, 'new_db_prefix' ) );
53
 
54
+ // search field should not be empty
55
  if ( '' === $replace ) {
56
  $this->add_error( esc_html__( 'Replace Field should not be empty.', 'search-and-replace' ) );
57
 
58
+ return false;
59
  }
60
 
61
+ // Do not pass the new db prefix if `change_db_prefix` isn't flagged.
62
+ // @codingStandardsIgnoreStart
63
+ $change_db_prefix = isset( $_POST[ 'change_db_prefix' ] ) ?
64
+ filter_var( $_POST[ 'change_db_prefix' ], FILTER_VALIDATE_BOOLEAN ) :
65
+ false;
66
+ // @codingStandardsIgnoreEnd
67
+
68
+ $new_db_prefix = $change_db_prefix ? $new_db_prefix : '';
69
+
70
+ // Make the backup.
71
+ $report = $this->dbe->db_backup( $search, $replace, [], true, $new_db_prefix );
72
+
73
+ // Show the replace report.
74
  $this->downloader->show_modal( $report );
75
 
76
+ return true;
77
  }
78
 
79
  /**
80
+ * Shows the page template
81
  */
82
  public function render() {
83
 
84
+ require_once __DIR__ . '/../templates/replace-domain.php';
85
  }
86
 
87
  /**
109
 
110
  return 'replace-domain-url';
111
  }
112
+ }
inc/Page/SearchReplace.php CHANGED
@@ -1,8 +1,8 @@
1
  <?php
 
2
  namespace Inpsyde\SearchReplace\Page;
3
 
4
- use Inpsyde\SearchReplace\Database,
5
- Inpsyde\SearchReplace\Service;
6
  use Inpsyde\SearchReplace\FileDownloader;
7
 
8
  /**
@@ -49,11 +49,32 @@ class SearchReplace extends AbstractPage implements PageInterface {
49
  }
50
 
51
  /**
52
- * shows the page contents
53
  */
54
  public function render() {
55
 
56
- require_once( __DIR__ . '/../templates/search_replace.php' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  }
58
 
59
  /**
@@ -74,106 +95,102 @@ class SearchReplace extends AbstractPage implements PageInterface {
74
  return 'search-replace';
75
  }
76
 
77
- /**
78
- *prints a select with all the tables and their sizes
79
- *
80
- * @return void
81
- */
82
- protected function show_table_list() {
83
-
84
- $tables = $this->dbm->get_tables();
85
- $sizes = $this->dbm->get_sizes();
86
- $table_count = count( $tables );
87
-
88
- //adjust height of select according to table count, but max 20 rows
89
- $select_rows = $table_count < 20 ? $table_count : 20;
90
-
91
- //if we come from a dry run, we select the tables to the dry run again
92
- /** @var bool | string $selected_tables */
93
- $selected_tables = FALSE;
94
- if ( isset( $_POST[ 'select_tables' ] ) ) {
95
- $selected_tables = $_POST[ 'select_tables' ];
96
- }
97
-
98
- echo '<select id="select_tables" name="select_tables[]" multiple="multiple" size = "' . $select_rows . '">';
99
- foreach ( $tables as $table ) {
100
- $table_size = isset ( $sizes[ $table ] ) ? $sizes[ $table ] : '';
101
- //check if dry run. if dry run && current table is in "selected" array add selected attribute
102
- if ( isset( $_POST[ 'dry_run' ] )
103
- && $selected_tables
104
- && in_array( $table, $selected_tables, FALSE )
105
- ) {
106
- echo "<option value='$table' selected='selected'>$table . $table_size </option>";
107
-
108
- //if current table had not been selected echo option without "selected" attribute
109
- } else {
110
- echo '<option value="' . $table . '">' . $table . $table_size . '</option>';
111
- }
112
-
113
- }
114
- echo( '</select>' );
115
- }
116
-
117
  /**
118
  * @return bool
 
119
  */
120
  public function save() {
121
 
122
- //check for errors in form
123
  if ( ! $this->is_request_valid() ) {
 
 
124
 
125
- $this->display_errors();
126
-
127
- return FALSE;
 
128
  }
129
 
130
- $tables = isset( $_POST[ 'select_tables' ] ) ? $_POST[ 'select_tables' ] : '';
131
- $dry_run = isset( $_POST[ 'dry_run' ] ) ? TRUE : FALSE;
132
 
133
- //remove wp_magic_quotes
134
  $search = stripslashes( filter_input( INPUT_POST, 'search' ) );
135
  $replace = stripslashes( filter_input( INPUT_POST, 'replace' ) );
 
 
136
 
137
- //if dry run is checked we run the replace function with dry run and return
138
- if ( TRUE === $dry_run ) {
139
- $this->run_replace( $search, $replace, $tables, $dry_run );
 
 
140
 
141
- return FALSE;
 
 
 
 
142
  }
143
 
144
  $export_or_save = filter_input( INPUT_POST, 'export_or_save' );
145
 
146
  if ( 'export' === $export_or_save ) {
147
- //'export'-button was checked
148
- $report = $this->dbe->db_backup( $search, $replace, $tables );
149
  $this->downloader->show_modal( $report );
150
  } else {
151
- //"Save changes to database" was checked
152
- $this->run_replace( $search, $replace, $tables, $dry_run );
153
  }
154
-
155
- return TRUE;
156
  }
157
 
158
  /**
159
- * @return string
 
 
160
  */
161
- protected function get_submit_button_title() {
162
 
163
- return esc_html__( 'Do Search & Replace', 'search-and-replace' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  }
165
 
166
  /**
167
- * calls run_replace_table() on each table provided in array $tables
168
  *
169
- * @param $search
170
- * @param $replace
171
- * @param $tables array of tables we want to search
172
- * @param $dry_run True if dry run (no changes are written to db)
 
173
  *
174
- * @return null
175
  */
176
- protected function run_replace( $search, $replace, $tables, $dry_run ) {
177
 
178
  echo '<div class="updated notice is-dismissible">';
179
  if ( $dry_run ) {
@@ -194,90 +211,77 @@ class SearchReplace extends AbstractPage implements PageInterface {
194
  }
195
  $this->replace->set_dry_run( $dry_run );
196
 
197
- $report = $this->replace->run_search_replace( $search, $replace, $tables );
198
 
199
  if ( is_wp_error( $report ) ) {
200
- $this->add_error( __( $report->get_error_message(), 'search-and-replace' ) );
201
- $this->display_errors();
202
  } else {
203
-
204
  if ( count( $report[ 'changes' ] ) > 0 ) {
205
  $this->downloader->show_changes( $report );
206
  }
207
 
208
- //if no changes found report that
209
  if ( 0 === count( $report [ 'changes' ] ) ) {
210
  echo '<p>' . esc_html__( 'Search pattern not found.', 'search-and-replace' ) . '</p>';
211
  }
212
  }
213
 
214
  echo '</div>';
215
-
216
  }
217
 
218
  /**
219
- * checks the input form and writes possible errors to a WP_Error object
220
  *
221
- * @return bool true|false
222
  */
223
- protected function is_request_valid() {
224
-
225
- $select_tables = filter_input( INPUT_POST, 'select_tables' );
226
- if ( '' === $select_tables ) {
227
- $this->add_error( __( 'No Tables were selected.', 'search-and-replace' ) );
228
-
229
- return FALSE;
230
- }
231
-
232
- $search = filter_input( INPUT_POST, 'search' );
233
- $replace = filter_input( INPUT_POST, 'replace' );
234
 
235
- //if search field is empty and replace field is not empty quit. If both fields are empty, go on (useful for backup of single tables without changing)
236
- if ( '' === $search && '' === $replace ) {
237
- $this->add_error( esc_attr__( 'Search field is empty.', 'search-and-replace' ) );
238
 
239
- return FALSE;
240
- }
 
 
241
 
242
- $export_or_save = filter_input( INPUT_POST, 'export_or_save' );
243
- //check if the user tries to replace domain name into the database
244
- if ( '' === $export_or_save || 'save_to_db' === $export_or_save ) {
245
- $contains_site_url = strpos( $search, $this->get_stripped_site_url() );
246
- if ( FALSE !== $contains_site_url ) {
247
- $this->add_error(
248
- esc_html__(
249
- 'Your search contains your current site url. Replacing your site url will most likely cause your site to break. If you want to change the URL (and you know what you doing), please use the export function and make sure you backup your database before reimporting the changed SQL.',
250
- 'search-and-replace'
251
- )
252
- );
253
-
254
- return FALSE;
255
- }
 
 
 
256
 
257
  }
258
-
259
-
260
-
261
- return TRUE;
262
  }
263
 
264
  /**
265
- * Returns the site url, strips http:// or https://
266
  */
267
- private function get_stripped_site_url() {
268
-
269
- $url = get_site_url();
270
 
271
- return substr( $url, strpos( $url, '/' ) + 2 );
272
  }
273
 
274
  /**
275
- * shows the search value in template.
276
  */
277
  private function get_search_value() {
278
 
279
  $search = isset( $_POST[ 'search' ] ) ? $_POST[ 'search' ] : '';
280
- $dry_run = isset( $_POST[ 'dry_run' ] ) ? TRUE : FALSE;
281
 
282
  if ( $dry_run ) {
283
  $search = stripslashes( $search );
@@ -288,12 +292,12 @@ class SearchReplace extends AbstractPage implements PageInterface {
288
  }
289
 
290
  /**
291
- * shows the replace value in template
292
  */
293
  private function get_replace_value() {
294
 
295
  $replace = isset( $_POST[ 'replace' ] ) ? $_POST[ 'replace' ] : '';
296
- $dry_run = isset( $_POST[ 'dry_run' ] ) ? TRUE : FALSE;
297
  if ( $dry_run ) {
298
  $replace = stripslashes( $replace );
299
  $replace = htmlentities( $replace );
@@ -302,4 +306,35 @@ class SearchReplace extends AbstractPage implements PageInterface {
302
 
303
  }
304
 
305
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
+
3
  namespace Inpsyde\SearchReplace\Page;
4
 
5
+ use Inpsyde\SearchReplace\Database;
 
6
  use Inpsyde\SearchReplace\FileDownloader;
7
 
8
  /**
49
  }
50
 
51
  /**
52
+ * Shows the page contents
53
  */
54
  public function render() {
55
 
56
+ require_once __DIR__ . '/../templates/search-replace.php';
57
+
58
+ wp_localize_script(
59
+ 'insr-js',
60
+ 'insr_data_obj', [
61
+ 'search_matches_site_url' => __(
62
+ 'Your search contains your current site url. Replacing your site url can cause your site to break. Are you sure you wish to proceed?',
63
+ 'search-and-replace'
64
+ ),
65
+ 'site_url' => $this->get_stripped_site_url(),
66
+ ]
67
+ );
68
+ }
69
+
70
+ /**
71
+ * Returns the site url, strips http:// or https://
72
+ */
73
+ private function get_stripped_site_url() {
74
+
75
+ $url = get_site_url();
76
+
77
+ return substr( $url, strpos( $url, '/' ) + 2 );
78
  }
79
 
80
  /**
95
  return 'search-replace';
96
  }
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  /**
99
  * @return bool
100
+ * @throws \Throwable
101
  */
102
  public function save() {
103
 
 
104
  if ( ! $this->is_request_valid() ) {
105
+ return false;
106
+ }
107
 
108
+ // Retrieve tables.
109
+ $tables = $this->selected_tables();
110
+ if ( ! $tables ) {
111
+ return false;
112
  }
113
 
114
+ // @codingStandardsIgnoreLine
115
+ $dry_run = isset( $_POST[ 'dry_run' ] ) ? true : false;
116
 
117
+ // remove wp_magic_quotes
118
  $search = stripslashes( filter_input( INPUT_POST, 'search' ) );
119
  $replace = stripslashes( filter_input( INPUT_POST, 'replace' ) );
120
+ $csv = stripslashes( filter_input( INPUT_POST, 'csv' ) );
121
+ $csv = ( $csv === '' ? null : $csv );
122
 
123
+ // Do not perform anything if we haven't anything.
124
+ if ( ( ! $search && ! $replace ) && ! $csv ) {
125
+ $this->add_error( esc_html__( 'You must provide at least a search string or a csv data', 'search-and-replace' ) );
126
+ return false;
127
+ }
128
 
129
+ // If dry run is checked we run the replace function with dry run and return
130
+ if ( true === $dry_run ) {
131
+ $this->run_replace( $search, $replace, $tables, $dry_run, $csv );
132
+
133
+ return false;
134
  }
135
 
136
  $export_or_save = filter_input( INPUT_POST, 'export_or_save' );
137
 
138
  if ( 'export' === $export_or_save ) {
139
+ // 'export'-button was checked
140
+ $report = $this->dbe->db_backup( $search, $replace, $tables, false, '', $csv );
141
  $this->downloader->show_modal( $report );
142
  } else {
143
+ // "Save changes to database" was checked
144
+ $this->run_replace( $search, $replace, $tables, $dry_run, $csv );
145
  }
146
+
147
+ return true;
148
  }
149
 
150
  /**
151
+ * Checks the input form and writes possible errors to a WP_Error object
152
+ *
153
+ * @return bool true|false
154
  */
155
+ protected function is_request_valid() {
156
 
157
+ // If not table are selected mark the request as invalid but let user know why.
158
+ if ( ! $this->selected_tables() ) {
159
+ $this->add_error(
160
+ esc_html__(
161
+ 'No Tables were selected. You must select at least one table to perform the action.',
162
+ 'search-and-replace'
163
+ )
164
+ );
165
+
166
+ return false;
167
+ }
168
+
169
+ $search = filter_input( INPUT_POST, 'search' );
170
+ $replace = filter_input( INPUT_POST, 'replace' );
171
+
172
+ // if search field is empty and replace field is not empty quit. If both fields are empty, go on (useful for backup of single tables without changing)
173
+ if ( '' === $search && '' !== $replace ) {
174
+ $this->add_error( esc_attr__( 'Search field is empty.', 'search-and-replace' ) );
175
+
176
+ return false;
177
+ }
178
+
179
+ return true;
180
  }
181
 
182
  /**
183
+ * Calls run_replace_table() on each table provided in array $tables.
184
  *
185
+ * @param string $search
186
+ * @param string $replace
187
+ * @param array $tables Array of tables we want to search.
188
+ * @param bool $dry_run True if dry run (no changes are written to db).
189
+ * @param bool $csv
190
  *
191
+ * @throws \Throwable
192
  */
193
+ protected function run_replace( $search, $replace, $tables, $dry_run, $csv = null ) {
194
 
195
  echo '<div class="updated notice is-dismissible">';
196
  if ( $dry_run ) {
211
  }
212
  $this->replace->set_dry_run( $dry_run );
213
 
214
+ $report = $this->replace->run_search_replace( $search, $replace, $tables, $csv );
215
 
216
  if ( is_wp_error( $report ) ) {
217
+ $this->add_error( $report->get_error_message() );
 
218
  } else {
 
219
  if ( count( $report[ 'changes' ] ) > 0 ) {
220
  $this->downloader->show_changes( $report );
221
  }
222
 
223
+ // if no changes found report that
224
  if ( 0 === count( $report [ 'changes' ] ) ) {
225
  echo '<p>' . esc_html__( 'Search pattern not found.', 'search-and-replace' ) . '</p>';
226
  }
227
  }
228
 
229
  echo '</div>';
 
230
  }
231
 
232
  /**
233
+ * Prints a select with all the tables and their sizes
234
  *
235
+ * @return void
236
  */
237
+ protected function show_table_list() {
 
 
 
 
 
 
 
 
 
 
238
 
239
+ $tables = $this->dbm->get_tables();
240
+ $sizes = $this->dbm->get_sizes();
241
+ $table_count = count( $tables );
242
 
243
+ // adjust height of select according to table count, but max 20 rows
244
+ $select_rows = $table_count < 20 ? $table_count : 20;
245
+ // if we come from a dry run, we select the tables to the dry run again
246
+ $selected_tables = $this->selected_tables();
247
 
248
+ echo '<select id="select_tables" name="select_tables[]" multiple="multiple" size = "' . $select_rows . '">';
249
+ foreach ( $tables as $table ) {
250
+ $table_size = isset ( $sizes[ $table ] ) ? $sizes[ $table ] : '';
251
+ // check if dry run. if dry run && current table is in "selected" array add selected attribute
252
+ $selected = ( isset( $_POST[ 'dry_run' ] )
253
+ && $selected_tables
254
+ && in_array( $table, $selected_tables, false )
255
+ )
256
+ ? 'selected="selected"'
257
+ : '';
258
+
259
+ printf(
260
+ "<option value='%s' %s>%s</option>",
261
+ esc_attr( $table ),
262
+ $selected,
263
+ esc_html( $table . $table_size )
264
+ );
265
 
266
  }
267
+ echo '</select>';
 
 
 
268
  }
269
 
270
  /**
271
+ * @return string
272
  */
273
+ protected function get_submit_button_title() {
 
 
274
 
275
+ return esc_html__( 'Do Search & Replace', 'search-and-replace' );
276
  }
277
 
278
  /**
279
+ * Shows the search value in template.
280
  */
281
  private function get_search_value() {
282
 
283
  $search = isset( $_POST[ 'search' ] ) ? $_POST[ 'search' ] : '';
284
+ $dry_run = isset( $_POST[ 'dry_run' ] ) ? true : false;
285
 
286
  if ( $dry_run ) {
287
  $search = stripslashes( $search );
292
  }
293
 
294
  /**
295
+ * Shows the replace value in template
296
  */
297
  private function get_replace_value() {
298
 
299
  $replace = isset( $_POST[ 'replace' ] ) ? $_POST[ 'replace' ] : '';
300
+ $dry_run = isset( $_POST[ 'dry_run' ] ) ? true : false;
301
  if ( $dry_run ) {
302
  $replace = stripslashes( $replace );
303
  $replace = htmlentities( $replace );
306
 
307
  }
308
 
309
+ /**
310
+ * Shows the csv value in template.
311
+ */
312
+ private function get_csv_value() {
313
+
314
+ $csv = isset( $_POST[ 'csv' ] ) ? $_POST[ 'csv' ] : '';
315
+ $dry_run = isset( $_POST[ 'dry_run' ] ) ? true : false;
316
+ if ( $dry_run ) {
317
+ $csv = stripslashes( $csv );
318
+ $csv = htmlentities( $csv );
319
+ echo $csv;
320
+ }
321
+
322
+ }
323
+
324
+ /**
325
+ * Retrieve Selected Tables
326
+ *
327
+ * @return array The tables list from the POST request
328
+ */
329
+ private function selected_tables() {
330
+
331
+ $tables = [];
332
+
333
+ if ( ! empty( $_POST[ 'select_tables' ] ) ) {
334
+ $tables = filter_var( $_POST[ 'select_tables' ], FILTER_SANITIZE_STRING, FILTER_REQUIRE_ARRAY );
335
+ }
336
+
337
+ return $tables;
338
+ }
339
+
340
+ }
inc/Page/SqlImport.php CHANGED
@@ -45,11 +45,11 @@ class SqlImport extends AbstractPage implements PageInterface {
45
  }
46
 
47
  /**
48
- * callback function for menu item
49
  */
50
  public function render() {
51
 
52
- require_once( __DIR__ . '/../templates/sql_import.php' );
53
  }
54
 
55
  /**
@@ -57,22 +57,25 @@ class SqlImport extends AbstractPage implements PageInterface {
57
  */
58
  protected function get_submit_button_title() {
59
 
60
- return __( 'Import SQL file', 'search-and-replace' );
61
  }
62
 
 
 
 
63
  public function save() {
64
 
65
- // TODO: Better handling of large files
66
  // maybe like here: http://stackoverflow.com/questions/147821/loading-sql-files-from-within-php , answer by user 'gromo'
67
  $php_upload_error_code = $_FILES[ 'file_to_upload' ][ 'error' ];
68
  if ( 0 === $php_upload_error_code ) {
69
-
70
- //get file extension
71
  $ext = strrchr( $_FILES [ 'file_to_upload' ][ 'name' ], '.' );
72
- //parse file
73
  $tempfile = $_FILES [ 'file_to_upload' ][ 'tmp_name' ];
74
  switch ( $ext ) {
75
  case '.sql':
 
76
  $sql_source = file_get_contents( $tempfile );
77
  break;
78
  case '.gz':
@@ -80,20 +83,19 @@ class SqlImport extends AbstractPage implements PageInterface {
80
  break;
81
  default:
82
  $this->add_error(
83
- __(
84
- 'The file has neither \'.gz\' nor \'.sql\' Extension. Import not possible.',
85
  'search-and-replace'
86
  )
87
  );
88
-
89
  return;
90
  }
91
 
92
- //call import function
93
  $success = $this->dbi->import_sql( $sql_source );
94
  if ( - 1 === $success ) {
95
  $this->add_error(
96
- __(
97
  'The file does not seem to be a valid SQL file. Import not possible.',
98
  'search-and-replace'
99
  )
@@ -101,50 +103,73 @@ class SqlImport extends AbstractPage implements PageInterface {
101
  } else {
102
  echo '<div class="updated notice is-dismissible">';
103
  echo '<p>';
104
- $msg = printf(
105
- __(
106
- 'The SQL file was successfully imported. %s SQL queries were performed.', 'search-and-replace'
 
 
107
  ),
108
- $success
109
  );
110
- echo esc_html( $msg );
111
  echo '</p></div>';
112
  }
113
  } else {
114
- //show error
115
  $php_upload_errors = array(
116
- 0 => 'There is no error, the file uploaded with success',
 
 
 
117
  1 => esc_html__(
118
- 'The uploaded file exceeds the upload_max_filesize directive in php.ini', 'search-and-replace'
 
119
  ),
120
  2 => esc_html__(
121
  'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
122
  'search-and-replace'
123
  ),
124
- 3 => esc_html__( 'The uploaded file was only partially uploaded', 'search-and-replace' ),
125
- 4 => esc_html__( 'No file was uploaded.', 'search-and-replace' ),
126
- 6 => esc_html__( 'Missing a temporary folder.', 'search-and-replace' ),
127
- 7 => esc_html__( 'Failed to write file to disk.', 'search-and-replace' ),
128
- 8 => esc_html__( 'A PHP extension stopped the file upload.', 'search-and-replace' ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  );
130
 
131
- $$this->add_error(
132
- __( 'Upload Error: ' . $php_upload_errors[ $php_upload_error_code ], 'search-and-replace' )
 
 
 
 
133
  );
134
  }
135
 
136
  }
137
 
138
  /**
139
- * reads a gz file into a string
140
  *
141
- * @param $filename String path ot file
142
  *
143
- * @return string The file contents
144
  */
145
  private function read_gzfile_into_string( $filename ) {
146
 
147
- $zd = gzopen( $filename, 'r' );
148
  $contents = gzread( $zd, 10000 );
149
  gzclose( $zd );
150
 
@@ -178,20 +203,21 @@ class SqlImport extends AbstractPage implements PageInterface {
178
  }
179
 
180
  /**
181
- * @param $size
182
  *
183
  * @return float
184
  */
185
  private function parse_size( $size ) {
186
 
187
- $unit = preg_replace( '/[^bkmgtpezy]/i', '', $size ); // Remove the non-unit characters from the size.
188
- $size = preg_replace( '/[^0-9\.]/', '', $size ); // Remove the non-numeric characters from the size.
 
 
189
  if ( $unit ) {
190
  // Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
191
- return round( $size * pow( 1024, stripos( 'bkmgtpezy', $unit[ 0 ] ) ) );
192
- } else {
193
- return round( $size );
194
  }
 
195
  }
196
 
197
- }
45
  }
46
 
47
  /**
48
+ * Callback function for menu item
49
  */
50
  public function render() {
51
 
52
+ require_once dirname(__DIR__) . '/templates/sql-import.php';
53
  }
54
 
55
  /**
57
  */
58
  protected function get_submit_button_title() {
59
 
60
+ return esc_html__( 'Import SQL file', 'search-and-replace' );
61
  }
62
 
63
+ /**
64
+ * {@inheritdoc}
65
+ */
66
  public function save() {
67
 
68
+ // @ToDo: Better handling of large files
69
  // maybe like here: http://stackoverflow.com/questions/147821/loading-sql-files-from-within-php , answer by user 'gromo'
70
  $php_upload_error_code = $_FILES[ 'file_to_upload' ][ 'error' ];
71
  if ( 0 === $php_upload_error_code ) {
72
+ // get file extension
 
73
  $ext = strrchr( $_FILES [ 'file_to_upload' ][ 'name' ], '.' );
74
+ // parse file
75
  $tempfile = $_FILES [ 'file_to_upload' ][ 'tmp_name' ];
76
  switch ( $ext ) {
77
  case '.sql':
78
+ // @codingStandardsIgnoreLine
79
  $sql_source = file_get_contents( $tempfile );
80
  break;
81
  case '.gz':
83
  break;
84
  default:
85
  $this->add_error(
86
+ esc_html__(
87
+ 'The file has neither \'.gz\' nor \'.sql\' Extension. Import not possible.',
88
  'search-and-replace'
89
  )
90
  );
 
91
  return;
92
  }
93
 
94
+ // call import function
95
  $success = $this->dbi->import_sql( $sql_source );
96
  if ( - 1 === $success ) {
97
  $this->add_error(
98
+ esc_html__(
99
  'The file does not seem to be a valid SQL file. Import not possible.',
100
  'search-and-replace'
101
  )
103
  } else {
104
  echo '<div class="updated notice is-dismissible">';
105
  echo '<p>';
106
+ printf(
107
+ // Translators: %s print the sql source.
108
+ esc_html__(
109
+ 'The SQL file was successfully imported. %s SQL queries were performed.',
110
+ 'search-and-replace'
111
  ),
112
+ esc_html($success)
113
  );
 
114
  echo '</p></div>';
115
  }
116
  } else {
117
+ // show error
118
  $php_upload_errors = array(
119
+ 0 => esc_html__(
120
+ 'There is no error, the file uploaded with success',
121
+ 'search-and-replace'
122
+ ),
123
  1 => esc_html__(
124
+ 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
125
+ 'search-and-replace'
126
  ),
127
  2 => esc_html__(
128
  'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
129
  'search-and-replace'
130
  ),
131
+ 3 => esc_html__(
132
+ 'The uploaded file was only partially uploaded',
133
+ 'search-and-replace'
134
+ ),
135
+ 4 => esc_html__(
136
+ 'No file was uploaded.',
137
+ 'search-and-replace'
138
+ ),
139
+ 6 => esc_html__(
140
+ 'Missing a temporary folder.',
141
+ 'search-and-replace'
142
+ ),
143
+ 7 => esc_html__(
144
+ 'Failed to write file to disk.',
145
+ 'search-and-replace' ),
146
+ 8 => esc_html__(
147
+ 'A PHP extension stopped the file upload.',
148
+ 'search-and-replace'
149
+ ),
150
  );
151
 
152
+ $this->add_error(
153
+ sprintf(
154
+ // Translators: %s print the error message.
155
+ esc_html__( 'Upload Error: %s', 'search-and-replace' ),
156
+ $php_upload_errors[ $php_upload_error_code ]
157
+ )
158
  );
159
  }
160
 
161
  }
162
 
163
  /**
164
+ * Reads a gz file into a string.
165
  *
166
+ * @param string $filename String path ot file.
167
  *
168
+ * @return string The file contents.
169
  */
170
  private function read_gzfile_into_string( $filename ) {
171
 
172
+ $zd = gzopen( $filename, 'r' );
173
  $contents = gzread( $zd, 10000 );
174
  gzclose( $zd );
175
 
203
  }
204
 
205
  /**
206
+ * @param int $size
207
  *
208
  * @return float
209
  */
210
  private function parse_size( $size ) {
211
 
212
+ // Remove the non-unit characters from the size.
213
+ $unit = preg_replace( '/[^bkmgtpezy]/i', '', $size );
214
+ // Remove the non-numeric characters from the size.
215
+ $size = preg_replace( '/[^0-9\.]/', '', $size );
216
  if ( $unit ) {
217
  // Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
218
+ return round( $size * ( 1024 ** stripos( 'bkmgtpezy', $unit[0] ) ) );
 
 
219
  }
220
+ return round( $size );
221
  }
222
 
223
+ }
inc/Plugin.php DELETED
@@ -1,48 +0,0 @@
1
- <?php
2
- namespace Inpsyde\SearchReplace;
3
-
4
- /**
5
- * Class Plugin
6
- *
7
- * @package Inpsyde\SearchReplace\inc
8
- */
9
- class Plugin {
10
-
11
- /**
12
- * @param string $file : The path to the Plugin main file
13
- */
14
- public function run() {
15
-
16
- global $wpdb;
17
-
18
- if ( is_admin() ) {
19
-
20
- // set max_execution_time to 0
21
- $RunTime = new Service\MaxExecutionTime();
22
- $RunTime->set();
23
-
24
- $dbm = new Database\Manager( $wpdb );
25
- $replace = new Database\Replace( $dbm );
26
- $dbe = new Database\Exporter( $replace, $dbm );
27
- $dbi = new Database\Importer();
28
-
29
- $downloader = new FileDownloader( $dbe );
30
- add_action( 'init', array( $downloader, 'deliver_backup_file' ) );
31
-
32
- $page_manager = new Page\Manager();
33
- $page_manager->add_page( new Page\BackupDatabase( $dbe, $downloader ) );
34
- $page_manager->add_page( new Page\SearchReplace( $dbm, $replace, $dbe, $downloader ) );
35
- $page_manager->add_page( new Page\ReplaceDomain( $dbm, $dbe, $downloader ) );
36
- $page_manager->add_page( new Page\SqlImport( $dbi ) );
37
- $page_manager->add_page( new Page\Credits() );
38
-
39
- add_action( 'admin_menu', array( $page_manager, 'register_pages' ) );
40
- add_action( 'admin_head', array( $page_manager, 'remove_submenu_pages' ) );
41
-
42
- add_action( 'admin_enqueue_scripts', array( $page_manager, 'register_css' ) );
43
- add_action( 'admin_enqueue_scripts', array( $page_manager, 'register_js' ) );
44
- }
45
-
46
- }
47
-
48
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/Service/MaxExecutionTime.php CHANGED
@@ -9,7 +9,7 @@ namespace Inpsyde\SearchReplace\Service;
9
  class MaxExecutionTime {
10
 
11
  /**
12
- * @var max_execution_time
13
  */
14
  private $met;
15
 
@@ -18,32 +18,32 @@ class MaxExecutionTime {
18
  *
19
  * @param int $time
20
  */
21
- public function set( $time = 0 ){
22
 
23
- if( $time == 0 ){
24
  $this->store();
25
  }
26
 
27
- set_time_limit( $time );
28
 
29
  }
30
 
31
  /**
32
- * Restor timelimit
33
  */
34
- public function restore(){
35
 
36
  $this->set( $this->met );
37
 
38
- }
39
 
40
  /**
41
- *
42
  */
43
- public function store(){
44
 
45
- $this->met = (int) ini_get('max_execution_time');
46
 
47
  }
48
 
49
- }
9
  class MaxExecutionTime {
10
 
11
  /**
12
+ * @var int
13
  */
14
  private $met;
15
 
18
  *
19
  * @param int $time
20
  */
21
+ public function set( $time = 0 ) {
22
 
23
+ if ( 0 === $time ) {
24
  $this->store();
25
  }
26
 
27
+ @set_time_limit( $time );
28
 
29
  }
30
 
31
  /**
32
+ * Restore timelimit.
33
  */
34
+ public function restore() {
35
 
36
  $this->set( $this->met );
37
 
38
+ }
39
 
40
  /**
41
+ * Fetch the max_execution_time from php.ini.
42
  */
43
+ public function store() {
44
 
45
+ $this->met = (int) ini_get( 'max_execution_time' );
46
 
47
  }
48
 
49
+ }
inc/requisite/src/Requisite/AutoLoaderInterface.php DELETED
@@ -1,17 +0,0 @@
1
- <?php # -*- coding: utf-8 -*-
2
-
3
- namespace Requisite;
4
-
5
- /**
6
- * Interface AutoLoaderInterface
7
- *
8
- * @package Requisite
9
- */
10
- interface AutoLoaderInterface {
11
-
12
- /**
13
- * @param Rule\AutoLoadRuleInterface $rule
14
- * @return void
15
- */
16
- public function addRule( Rule\AutoLoadRuleInterface $rule );
17
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/requisite/src/Requisite/Loader/DefaultConditionalFileLoader.php DELETED
@@ -1,27 +0,0 @@
1
- <?php # -*- coding: utf-8 -*-
2
-
3
- namespace Requisite\Loader;
4
-
5
- /**
6
- * Class DefaultConditionalFileLoader
7
- *
8
- * Loads a given file if it is_readable().
9
- *
10
- * @package Requisite\Loader
11
- */
12
- class DefaultConditionalFileLoader implements FileLoaderInterface {
13
-
14
- /**
15
- * @param string $file
16
- * @return bool
17
- */
18
- public function loadFile( $file ) {
19
-
20
- if ( ! is_readable( $file ) )
21
- return FALSE;
22
-
23
- require_once $file;
24
- return TRUE;
25
- }
26
-
27
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/requisite/src/Requisite/Loader/DirectoryCacheFileLoader.php DELETED
@@ -1,82 +0,0 @@
1
- <?php # -*- coding: utf-8 -*-
2
-
3
- namespace Requisite\Loader;
4
-
5
- /**
6
- * Class DirectoryCacheFileLoader
7
- *
8
- * reads in a whole directory at once to cache the existing files
9
- * to avoid frequently usage of file_exists() checks
10
- *
11
- * @package Requisite\Loader
12
- */
13
- class DirectoryCacheFileLoader implements FileLoaderInterface {
14
-
15
- /**
16
- * @type array
17
- */
18
- private $files = array();
19
-
20
- /**
21
- * @type string
22
- */
23
- private $extension;
24
-
25
- /**
26
- * @tpye string
27
- */
28
- private $base_dir;
29
-
30
- /**
31
- * @param string $base_dir
32
- * @param string $extension
33
- */
34
- function __construct( $base_dir, $extension = '.php' ) {
35
-
36
- $this->base_dir = (string) $base_dir;
37
- $this->extension = (string) $extension;
38
- }
39
-
40
- /**
41
- * @param string $file
42
- * @return bool
43
- */
44
- public function loadFile( $file ) {
45
-
46
- if ( empty( $this->files ) )
47
- $this->files = $this->readDirRecursive(
48
- $this->base_dir,
49
- '*' . $this->extension
50
- );
51
-
52
- if ( ! in_array( $file, $this->files ) )
53
- return FALSE;
54
-
55
- require_once $file;
56
- return TRUE;
57
- }
58
-
59
- /**
60
- * read the subdirectory recursive and catch all files with the
61
- * given pattern
62
- *
63
- * Will return an array with the pattern as element if no file exists
64
- *
65
- * @param $dir
66
- * @param $pattern
67
- * @return array
68
- */
69
- public function readDirRecursive( $dir, $pattern ) {
70
-
71
- $sub_dirs = glob( $dir . '/*', \GLOB_ONLYDIR );
72
- $files = array();
73
- if ( ! empty( $sub_dirs ) ) {
74
- foreach ( $sub_dirs as $sub_dir ) {
75
- $files = array_merge( $files, $this->readDirRecursive( $sub_dir, $pattern ) );
76
- }
77
- }
78
- $files = array_merge( $files, glob( $dir . '/' . $pattern, \GLOB_NOCHECK ) );
79
-
80
- return $files;
81
- }
82
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/requisite/src/Requisite/Loader/FileLoaderInterface.php DELETED
@@ -1,19 +0,0 @@
1
- <?php # -*- coding: utf-8 -*-
2
-
3
- namespace Requisite\Loader;
4
-
5
- /**
6
- * Interface FileLoaderInterface
7
- *
8
- * Loads a given file, if exists.
9
- *
10
- * @package Requisite\Loader
11
- */
12
- interface FileLoaderInterface {
13
-
14
- /**
15
- * @param string $file
16
- * @return bool
17
- */
18
- public function loadFile( $file );
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/requisite/src/Requisite/Requisite.php DELETED
@@ -1,72 +0,0 @@
1
- <?php # -*- coding: utf-8 -*-
2
-
3
- namespace Requisite;
4
-
5
- /**
6
- * Class Requisite
7
- *
8
- * This class is just a static wrapper to load Requisite
9
- * if it is not used with an auto loader itself.
10
- *
11
- * @package Requisite
12
- */
13
- class Requisite {
14
-
15
- /**
16
- * @type bool
17
- */
18
- private static $is_loaded = FALSE;
19
-
20
- /**
21
- * @param string $base_dir (Optional, default to __DIR__ which is normally src/Requisite)
22
- */
23
- public static function init( $base_dir = '' ) {
24
-
25
- if ( self::$is_loaded )
26
- return;
27
-
28
- if ( empty( $base_dir ) )
29
- $base_dir = __DIR__;
30
- $base_dir = rtrim( $base_dir, '/\\' );
31
- $classes = self::get_classes();
32
-
33
- foreach ( $classes as $class => $file ) {
34
- if ( ! class_exists( $class ) )
35
- require_once $base_dir . $file;
36
- }
37
-
38
- self::$is_loaded = TRUE;
39
- }
40
-
41
- /**
42
- * @return array
43
- */
44
- private static function get_classes() {
45
-
46
- return array(
47
- __NAMESPACE__ . '\Loader\FileLoaderInterface'
48
- => '/Loader/FileLoaderInterface.php',
49
-
50
- __NAMESPACE__ . '\Loader\DefaultConditionalFileLoader'
51
- => '/Loader/DefaultConditionalFileLoader.php',
52
-
53
- __NAMESPACE__ . '\Loader\DirectoryCacheFileLoader'
54
- => '/Loader/DirectoryCacheFileLoader.php',
55
-
56
- __NAMESPACE__ . '\Rule\AutoLoadRuleInterface'
57
- => '/Rule/AutoLoadRuleInterface.php',
58
-
59
- __NAMESPACE__ . '\Rule\Psr4'
60
- => '/Rule/Psr4.php',
61
-
62
- __NAMESPACE__ . '\Rule\NamespaceDirectoryMapper'
63
- => '/Rule/NamespaceDirectoryMapper.php',
64
-
65
- __NAMESPACE__ . '\AutoLoaderInterface'
66
- => '/AutoLoaderInterface.php',
67
-
68
- __NAMESPACE__ . '\SPLAutoLoader'
69
- => '/SPLAutoLoader.php'
70
- );
71
- }
72
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/requisite/src/Requisite/Rule/AutoLoadRuleInterface.php DELETED
@@ -1,22 +0,0 @@
1
- <?php # -*- coding: utf-8 -*-
2
-
3
- namespace Requisite\Rule;
4
-
5
- /**
6
- * Interface AutoLoadRuleInterface
7
- *
8
- * AutoLoad rules are responsible to locate concrete files by a given,
9
- * fully qualified class names and load this class, if exists.
10
- * In a typical Requisite implementation they use Instances of
11
- * Requisite\Loader\FileLoaderInterface for that.
12
- *
13
- * @package Requisite\Rule
14
- */
15
- interface AutoLoadRuleInterface {
16
-
17
- /**
18
- * @param string $class
19
- * @return bool
20
- */
21
- public function loadClass( $class );
22
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/requisite/src/Requisite/Rule/NamespaceDirectoryMapper.php DELETED
@@ -1,13 +0,0 @@
1
- <?php # -*- coding: utf-8 -*-
2
-
3
- namespace Requisite\Rule;
4
-
5
- /**
6
- * Class NamespaceDirectoryMapper
7
- *
8
- * Alias of Psr4, provided for backward compatibility
9
- *
10
- * @deprecated
11
- * @package Requisite\Rule
12
- */
13
- class NamespaceDirectoryMapper extends Psr4 implements AutoLoadRuleInterface {}
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/requisite/src/Requisite/Rule/Psr4.php DELETED
@@ -1,82 +0,0 @@
1
- <?php # -*- coding: utf-8 -*-
2
-
3
- namespace Requisite\Rule;
4
-
5
- use
6
- Requisite\Loader;
7
-
8
- /**
9
- * Class Psr4
10
- *
11
- * Mapping a namespace structure to a directory structure
12
- * following the Psr4 standard
13
- *
14
- * @package Requisite\Rule
15
- */
16
- class Psr4 implements AutoLoadRuleInterface {
17
-
18
- /**
19
- * @type Loader\FileLoaderInterface
20
- */
21
- private $file_loader;
22
-
23
- /**
24
- * @type string
25
- */
26
- private $base_ns;
27
-
28
- /**
29
- * @type string
30
- */
31
- private $base_dir;
32
-
33
- /**
34
- * @param string $base_dir
35
- * @param string $base_ns
36
- * @param Loader\FileLoaderInterface $file_loader $file_loader (Optional)
37
- */
38
- function __construct( $base_dir, $base_ns = '', Loader\FileLoaderInterface $file_loader = NULL ) {
39
-
40
- // trim potential trailing slashes
41
- $this->base_dir = rtrim( (string) $base_dir, '\\/' );
42
-
43
- // always absolute namespaces with trailing slash
44
- // trim slashes AND spaces
45
- $base_ns = trim( $base_ns, '\\ ' );
46
- if ( ! empty( $base_ns ) )
47
- $base_ns = '\\' . $base_ns . '\\';
48
- else
49
- $base_ns = '\\';
50
- $this->base_ns = $base_ns;
51
-
52
- if ( ! $file_loader )
53
- $this->file_loader = new Loader\DirectoryCacheFileLoader( $this->base_dir );
54
- else
55
- $this->file_loader = $file_loader;
56
- }
57
-
58
- /**
59
- * @param string $class
60
- * @return bool
61
- */
62
- public function loadClass( $class ) {
63
-
64
- //make sure the class name is absolute
65
- if ( 0 !== strpos( $class, '\\' ) )
66
- $class = '\\' . $class;
67
-
68
- // check if the namespace matches the class
69
- if ( 0 !== strpos( $class, $this->base_ns ) )
70
- return FALSE;
71
-
72
- // strip the base namespace from the beginning of the class name
73
- if ( $this->base_ns === substr( $class, 0, strlen( $this->base_ns ) ) )
74
- $class = substr( $class, strlen( $this->base_ns ) );
75
-
76
- $class = ltrim( $class, '\\' );
77
- $class = str_replace( '\\', '/', $class );
78
- $file = $this->base_dir . '/' . $class . '.php';
79
-
80
- return $this->file_loader->loadFile( $file );
81
- }
82
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/requisite/src/Requisite/SPLAutoLoader.php DELETED
@@ -1,57 +0,0 @@
1
- <?php # -*- coding: utf-8 -*-
2
-
3
- namespace Requisite;
4
-
5
- /**
6
- * Class SPLAutoLoader
7
- *
8
- * @package Requisite
9
- */
10
- class SPLAutoLoader implements AutoLoaderInterface {
11
-
12
- /**
13
- * @type array
14
- */
15
- private $rules = array();
16
-
17
- /**
18
- * add a new instance to the spl load stack
19
- *
20
- * @param bool $append (Optional, default TRUE)
21
- * @param bool $throws (Optional, default FALSE)
22
- */
23
- public function __construct( $append = TRUE, $throws = FALSE ) {
24
-
25
- spl_autoload_register( array( $this, 'load' ), $throws, $append );
26
- }
27
-
28
- /**
29
- * @param string $class
30
- * @return bool
31
- */
32
- public function load( $class ) {
33
-
34
- foreach ( $this->rules as $rule )
35
- if ( $rule->loadClass( (string) $class ) )
36
- return TRUE;
37
-
38
- return FALSE;
39
- }
40
-
41
- /**
42
- * @param Rule\AutoLoadRuleInterface $rule
43
- * @return void
44
- */
45
- public function addRule( Rule\AutoLoadRuleInterface $rule ) {
46
-
47
- $this->rules[] = $rule;
48
- }
49
-
50
- /**
51
- * remove this instance from the spl load stack
52
- */
53
- public function unregister() {
54
-
55
- spl_autoload_unregister( array( $this, 'load' ) );
56
- }
57
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/templates/credits.php CHANGED
@@ -2,6 +2,7 @@
2
  /**
3
  * Template for displaying sql export page
4
  */
 
5
  // Prevent direct access.
6
  if ( ! defined( 'SEARCH_REPLACE_BASEDIR' ) ) {
7
  echo "Hi there! I'm just a part of plugin, not much I can do when called directly.";
@@ -9,45 +10,129 @@ if ( ! defined( 'SEARCH_REPLACE_BASEDIR' ) ) {
9
  }
10
  ?>
11
 
12
- <h2><?php esc_html_e( 'Hey nice to have you here!', 'search-and-replace' ); ?></h2>
13
- <p><?php printf(
14
- __( 'Search and Replace is refactored in 2015 by <a href="%1$s">Inpsyde GmbH</a>, maintained since 2006 and based on the original from <a href="%2$s">Mark Cunningham</a>.', 'search-and-replace' ),
15
- 'http://inpsyde.com/',
16
- 'http://thedeadone.net'
17
- ); ?></p>
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- <h2><?php esc_html_e( 'You rock! contribute the plugin.', 'search-and-replace' ); ?></h2>
20
- <p><?php printf(
21
- __( 'You can contribute the Plugin go to the repository on <a href="%s">github</a> making changes, create issues or submitting changes.', 'search-and-replace' ),
22
- 'https://github.com/inpsyde/search-and-replace/'
23
- ); ?></p>
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
- <h2><?php esc_html_e( 'We are Inpsyde', 'search-and-replace' ); ?></h2>
26
- <p><?php esc_html_e( 'Inpsyde has developed enterprise solutions with the world’s most popular open-source CMS since it was a kitten. Still do, inconvincible convinced.', 'search-and-replace' ); ?></p>
27
- <p><?php printf(
28
- __( 'Inpsyde is a WordPress <a href="%1$s">VIP Service Partner</a> and <a href="%2$s">WooCommerce Expert</a>.', 'search-and-replace' ),
29
- 'https://vip.wordpress.com/partner/inpsyde/',
30
- 'https://www.woothemes.com/experts/inpsyde-gmbh/'
31
- ); ?></p>
32
- <p><?php printf(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  __( 'Look at our other <a href="%s">free WordPress plugins</a>.', 'search-and-replace' ),
34
- 'https://profiles.wordpress.org/inpsyde/#content-plugins'
35
- ); ?></p>
36
-
 
 
 
 
 
 
 
37
 
38
- <h2><?php esc_html_e( 'Working at Inpsyde', 'search-and-replace' ); ?></h2>
39
- <p><?php esc_html_e( 'The biggest WordPress enterprise in Europe we’re dynamically growing and constantly looking for new employees. So do you want to shape WordPress in an interesting and exciting working environment? Here we are!', 'search-and-replace' ); ?> </p>
40
- <p><?php esc_html_e( 'At the moment we’re looking for developers for WordPress based products and services. If you’re not a developer and want to be part of us, we’d be happy to recieve your unsolicited application. At Inpsyde you can expect an open, modern and lively company culture:', 'search-and-replace' ); ?> </p>
41
- <ol>
42
- <li><?php esc_html_e( 'challenging and exciting projects', 'search-and-replace' ); ?></li>
43
- <li><?php esc_html_e( 'flexible working hours in remote office', 'search-and-replace' ); ?></li>
44
- <li><?php esc_html_e( 'deliberately flat hierarchies and short decision paths', 'search-and-replace' ); ?></li>
45
- <li><?php esc_html_e( 'a wide variety of tasks', 'search-and-replace' ); ?></li>
46
- <li><?php esc_html_e( 'freedom for personal development and responsible, self-reliant action', 'search-and-replace' ); ?></li>
47
 
48
- </ol>
49
- <p><?php printf(
50
- __( 'If you love open source and especially WordPress, if you love to organize your working days by yourself and want to use your pragmatic problem-solving skills and result-oriented work methods: <a href="%s">join our team</a>!', 'search-and-replace' ),
51
- 'http://inpsyde.com/#jobs'
52
- ); ?></p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  /**
3
  * Template for displaying sql export page
4
  */
5
+
6
  // Prevent direct access.
7
  if ( ! defined( 'SEARCH_REPLACE_BASEDIR' ) ) {
8
  echo "Hi there! I'm just a part of plugin, not much I can do when called directly.";
10
  }
11
  ?>
12
 
13
+ <h2><?php esc_html_e( 'Hey nice to have you here!', 'search-and-replace' ); ?></h2>
14
+ <p>
15
+ <?php printf(
16
+ wp_kses(
17
+ __(
18
+ 'Search and Replace is refactored in 2015 by <a href="%1$s">Inpsyde GmbH</a>, maintained since 2006 and based on the original from <a href="%2$s">Mark Cunningham</a>.',
19
+ 'search-and-replace'
20
+ ),
21
+ [
22
+ 'a' => [
23
+ 'href' => true,
24
+ ],
25
+ ]
26
+ ),
27
+ 'http://inpsyde.com/',
28
+ 'http://thedeadone.net'
29
+ );
30
+ ?>
31
+ </p>
32
 
33
+ <h2><?php esc_html_e( 'You rock! contribute the plugin.', 'search-and-replace' ); ?></h2>
34
+ <p>
35
+ <?php printf(
36
+ wp_kses(
37
+ __(
38
+ 'You can contribute the Plugin go to the repository on <a href="%s">github</a> making changes, creating issues, or submitting changes.',
39
+ 'search-and-replace'
40
+ ),
41
+ [
42
+ 'a' => [
43
+ 'href' => true,
44
+ ],
45
+ ]
46
+ ),
47
+ 'https://github.com/inpsyde/search-and-replace/'
48
+ );
49
+ ?>
50
+ </p>
51
 
52
+ <h2><?php esc_html_e( 'We are Inpsyde', 'search-and-replace' ); ?></h2>
53
+ <p>
54
+ <?php esc_html_e(
55
+ 'Inpsyde has developed enterprise solutions with the world’s most popular open-source CMS ever since it was a kitten. We still do, inconvincibly convinced.',
56
+ 'search-and-replace'
57
+ );
58
+ ?>
59
+ </p>
60
+ <p>
61
+ <?php printf(
62
+ wp_kses(
63
+ __(
64
+ 'Inpsyde is a WordPress <a href="%1$s">VIP Service Partner</a> and <a href="%2$s">WooCommerce Expert</a>.',
65
+ 'search-and-replace'
66
+ ),
67
+ [
68
+ 'a' => [
69
+ 'href' => true,
70
+ ],
71
+ ]
72
+ ),
73
+ 'https://vip.wordpress.com/partner/inpsyde/',
74
+ 'https://www.woothemes.com/experts/inpsyde-gmbh/'
75
+ );
76
+ ?>
77
+ </p>
78
+ <p>
79
+ <?php printf(
80
+ wp_kses(
81
  __( 'Look at our other <a href="%s">free WordPress plugins</a>.', 'search-and-replace' ),
82
+ [
83
+ 'a' => [
84
+ 'href' => true,
85
+ ],
86
+ ]
87
+ ),
88
+ esc_url( 'https://profiles.wordpress.org/inpsyde/#content-plugins' )
89
+ );
90
+ ?>
91
+ </p>
92
 
 
 
 
 
 
 
 
 
 
93
 
94
+ <h2><?php esc_html_e( 'Working at Inpsyde', 'search-and-replace' ); ?></h2>
95
+ <p>
96
+ <?php esc_html_e(
97
+ 'As the biggest WordPress enterprise in Europe, we’re dynamically growing and constantly looking for new employees. So, do you want to shape WordPress in an interesting and exciting work environment? Here we are!',
98
+ 'search-and-replace'
99
+ );
100
+ ?>
101
+ </p>
102
+ <p>
103
+ <?php esc_html_e(
104
+ 'At the moment we’re looking for developers for WordPress based products and services. If you’re not a developer and want to be part of us, we’d be happy to recieve your unsolicited application. At Inpsyde you can expect an open, modern and lively company culture:',
105
+ 'search-and-replace'
106
+ );
107
+ ?>
108
+ </p>
109
+ <ol>
110
+ <li><?php esc_html_e( 'challenging and exciting projects', 'search-and-replace' ); ?></li>
111
+ <li><?php esc_html_e( 'flexible working hours in remote office', 'search-and-replace' ); ?></li>
112
+ <li><?php esc_html_e( 'deliberately flat hierarchies and short decision paths', 'search-and-replace' ); ?></li>
113
+ <li><?php esc_html_e( 'a wide variety of tasks', 'search-and-replace' ); ?></li>
114
+ <li><?php esc_html_e(
115
+ 'freedom for personal development and responsible, self-reliant action',
116
+ 'search-and-replace'
117
+ ); ?>
118
+ </li>
119
 
120
+ </ol>
121
+ <p>
122
+ <?php printf(
123
+ wp_kses(
124
+ /* translators: $1 is a url */
125
+ __(
126
+ 'If you love open source and especially WordPress, if you love to organize your working days by yourself, and you want to use your pragmatic problem-solving skills and result-oriented work methods: <a href="%s">join our team</a>!',
127
+ 'search-and-replace'
128
+ ),
129
+ [
130
+ 'a' => [
131
+ 'href' => true,
132
+ ],
133
+ ]
134
+ ),
135
+ 'http://inpsyde.com/#jobs'
136
+ );
137
+ ?>
138
+ </p>
inc/templates/{db_backup.php → db-backup.php} RENAMED
@@ -2,6 +2,7 @@
2
  /**
3
  * Template for displaying sql export page
4
  */
 
5
  // Prevent direct access.
6
  if ( ! defined( 'SEARCH_REPLACE_BASEDIR' ) ) {
7
  echo "Hi there! I'm just a part of plugin, not much I can do when called directly.";
@@ -9,11 +10,13 @@ if ( ! defined( 'SEARCH_REPLACE_BASEDIR' ) ) {
9
  }
10
  ?>
11
 
12
- <p><?php esc_html_e(
13
- 'Create a backup of your database by clicking "Create SQL File".',
14
- 'search-and-replace' ); ?>
15
- </p>
 
 
16
 
17
- <form action="" method="post">
18
- <?php $this->show_submit_button(); ?>
19
- </form>
2
  /**
3
  * Template for displaying sql export page
4
  */
5
+
6
  // Prevent direct access.
7
  if ( ! defined( 'SEARCH_REPLACE_BASEDIR' ) ) {
8
  echo "Hi there! I'm just a part of plugin, not much I can do when called directly.";
10
  }
11
  ?>
12
 
13
+ <p>
14
+ <?php esc_html_e(
15
+ 'Create a backup of your database by clicking "Create SQL File".',
16
+ 'search-and-replace'
17
+ ); ?>
18
+ </p>
19
 
20
+ <form action="" method="post">
21
+ <?php $this->show_submit_button(); ?>
22
+ </form>
inc/templates/replace-domain.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template for displaying replace domain page
4
+ */
5
+
6
+ // Prevent direct access.
7
+ if ( ! defined( 'SEARCH_REPLACE_BASEDIR' ) ) {
8
+ echo "Hi there! I'm just a part of plugin, not much I can do when called directly.";
9
+ exit;
10
+ }
11
+ ?>
12
+ <p>
13
+ <?php esc_html_e(
14
+ 'If you want to migrate your site to another domain, enter the new URL in the field "Replace with" and click "Do Replace Domain/Url". You can then download a database backup containing the new URL.',
15
+ 'search-and-replace'
16
+ ); ?>
17
+ </p>
18
+
19
+ <form action="" method="post">
20
+
21
+ <table class="form-table">
22
+ <tbody>
23
+
24
+ <tr>
25
+ <th>
26
+ <label for="search">
27
+ <?php esc_html_e( 'Search for: ', 'search-and-replace' ); ?>
28
+ </label>
29
+ </th>
30
+ <td>
31
+ <input id="search" type="text" name="search" value="<?php esc_url( get_site_url() ); ?>" />
32
+ </td>
33
+ </tr>
34
+
35
+ <tr>
36
+ <th>
37
+ <label for="replace">
38
+ <?php esc_html_e( 'Replace with: ', 'search-and-replace' ); ?>
39
+ </label>
40
+ </th>
41
+ <td>
42
+ <input
43
+ id="replace"
44
+ type="text"
45
+ name="replace"
46
+ placeholder="<?php esc_attr_e( 'New URL', 'search-and-replace' ); ?>"
47
+ />
48
+ </td>
49
+ </tr>
50
+
51
+ <tr>
52
+ <th>
53
+ <label for="change_db_prefix">
54
+ <?php esc_html_e( 'Change database prefix', 'search-and-replace' ); ?>
55
+ </label>
56
+ </th>
57
+ <td><input id="change_db_prefix" type="checkbox" name="change_db_prefix" /></td>
58
+ </tr>
59
+
60
+ <tr class="disabled">
61
+ <th>
62
+ <label for="current_db_prefix">
63
+ <?php esc_html_e( 'Current prefix: ', 'search-and-replace' ); ?>
64
+ </label>
65
+ </th>
66
+ <td>
67
+ <?php echo esc_html( $this->dbm->get_base_prefix() ); ?>
68
+ </td>
69
+ </tr>
70
+
71
+ <tr class="maybe_disabled disabled">
72
+ <th>
73
+ <label for="new_db_prefix">
74
+ <?php esc_html_e( 'New prefix: ', 'search-and-replace' ); ?>
75
+ </label>
76
+ </th>
77
+ <td>
78
+ <input
79
+ id="new_db_prefix"
80
+ type="text"
81
+ name="new_db_prefix"
82
+ disabled
83
+ placeholder="<?php esc_attr_e( 'E.g new_', 'search-and-replace' ); ?>"
84
+ />
85
+ <p>
86
+ <?php esc_html_e( 'Underscore suffix "_" can be omitted', 'search-and-replace' ); ?>
87
+ </p>
88
+ </td>
89
+ </tr>
90
+ </tbody>
91
+ </table>
92
+ <?php $this->show_submit_button(); ?>
93
+ </form>
inc/templates/replace_domain.php DELETED
@@ -1,42 +0,0 @@
1
- <?php
2
- /**
3
- * Template for displaying replace domain page
4
- */
5
- // Prevent direct access.
6
- if ( ! defined( 'SEARCH_REPLACE_BASEDIR' ) ) {
7
- echo "Hi there! I'm just a part of plugin, not much I can do when called directly.";
8
- exit;
9
- }
10
- ?>
11
- <p><?php esc_html_e( 'If you want to migrate your site to another domain, enter the new URL in the field "Replace with" and create a backup of your database by clicking "Do Replace Domain/Url".',
12
- 'search-and-replace' ); ?> </p>
13
-
14
- <form action="" method="post">
15
-
16
- <table class="form-table">
17
- <tbody>
18
-
19
- <tr>
20
- <th><label for="search"><strong><?php esc_html_e( 'Search for: ', 'search-and-replace' ); ?></strong></label></th>
21
- <td><input id="search" type="text" disabled="disabled" name="search" value="<?php echo get_site_url(); ?>" /></td>
22
- </tr>
23
- <tr>
24
- <th><label for="replace"><strong><?php esc_html_e( 'Replace with: ', 'search-and-replace' ); ?></strong></label></th>
25
- <td><input id="replace" type="url" name="replace" placeholder="<?php esc_attr_e( 'New URL' ) ?>" /></td>
26
- </tr>
27
- <tr>
28
- <th><label for="change_db_prefix"><strong><?php esc_html_e( 'Change database prefix', 'search-and-replace' ); ?></strong></label></th>
29
- <td><input id ="change_db_prefix" type="checkbox" name="change_db_prefix" /></td>
30
- </tr>
31
- <tr class="disabled">
32
- <th><label for="current_db_prefix"><strong><?php esc_html_e( 'Current prefix: ', 'search-and-replace' ); ?></strong></label></th>
33
- <td><?php echo $this->dbm->get_base_prefix(); ?></td>
34
- </tr>
35
- <tr class="maybe_disabled disabled">
36
- <th><label for="new_db_prefix"><strong><?php esc_html_e( 'New prefix: ', 'search-and-replace' ); ?></strong></label></th>
37
- <td><input id="new_db_prefix" type="text" name="new_db_prefix" disabled placeholder="<?php esc_attr_e( 'New database prefix', 'search-and-replace' ) ?>" /></td>
38
- </tr>
39
- </tbody>
40
- </table>
41
- <?php $this->show_submit_button(); ?>
42
- </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/templates/search-replace.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template for displaying search & replace page
4
+ */
5
+
6
+ // Prevent direct access.
7
+ if ( ! defined( 'SEARCH_REPLACE_BASEDIR' ) ) {
8
+ echo "Hi there! I'm just a part of plugin, not much I can do when called directly.";
9
+ exit;
10
+ }
11
+ ?>
12
+ <form action="" method="post">
13
+ <table class="form-table">
14
+ <tbody>
15
+ <tr>
16
+ <th>
17
+ <label for="search">
18
+ <?php esc_html_e( 'Search for: ', 'search-and-replace' ); ?>
19
+ </label>
20
+ </th>
21
+ <td>
22
+ <textarea id="search" type="text" name="search" rows="1"><?php $this->get_search_value() ?></textarea>
23
+ </td>
24
+ </tr>
25
+ <tr>
26
+ <th>
27
+ <label for="replace">
28
+ <?php esc_html_e( 'Replace with: ', 'search-and-replace' ); ?>
29
+ </label>
30
+ </th>
31
+ <td>
32
+ <input id="replace" type="text" name="replace" value="<?php $this->get_replace_value() ?>" />
33
+ </td>
34
+ </tr>
35
+ <tr>
36
+ <th>
37
+ <label for="csv">
38
+ <?php esc_html_e( 'CSV Format Search/Replace:', 'search-and-replace' ); ?>
39
+ </label>
40
+ </th>
41
+ <td>
42
+ <textarea id="csv" cols="46" rows="5" name="csv" placeholder="<?php esc_html_e(
43
+ 'search value, replace value (one per line)',
44
+ 'search-and-replace'
45
+ ); ?>"><?php $this->get_csv_value(); ?></textarea>
46
+ <p id="csv-hint">
47
+ <?php esc_html_e(
48
+ 'Using comma delimited( , ). For example to replace cat with dog: cat,dog',
49
+ 'search-and-replace'
50
+ ); ?>
51
+ </p>
52
+ </td>
53
+ </tr>
54
+ <tr>
55
+ <th><strong><?php esc_html_e( 'Select tables', 'search-and-replace' ); ?></strong></th>
56
+ <td>
57
+ <?php $this->show_table_list(); ?>
58
+ <p>
59
+ <input id="select_all_tables" type="checkbox" name="select_all" />
60
+ <label for="select_all_tables">
61
+ <?php esc_html_e( 'Select all tables', 'search-and-replace' ); ?>
62
+ </label>
63
+ </p>
64
+ </td>
65
+ </tr>
66
+
67
+ <tr>
68
+ <th>
69
+ <label for="dry_run">
70
+ <?php esc_html_e( 'Dry Run', 'search-and-replace' ); ?>
71
+ </label>
72
+ </th>
73
+ <td>
74
+ <input type="checkbox" id="dry_run" name="dry_run" checked />
75
+ </td>
76
+ </tr>
77
+ <tr class="maybe_disabled disabled">
78
+ <th>
79
+ <?php esc_html_e( 'Export SQL file or write changes to DB?', 'search-and-replace' ); ?>
80
+ </th>
81
+ <td>
82
+ <p>
83
+ <input id="radio1" type="radio" name="export_or_save" value="export" checked disabled />
84
+ <label for="radio1">
85
+ <?php esc_html_e( 'Export SQL file with changes', 'search-and-replace' ); ?>
86
+ </label>
87
+ </p>
88
+ <p>
89
+ <input id="radio2" type="radio" name="export_or_save" value="save_to_db" disabled />
90
+ <label for="radio2">
91
+ <?php esc_html_e( 'Save changes to Database', 'search-and-replace' ); ?>
92
+ </label>
93
+ </p>
94
+ </td>
95
+ </tr>
96
+ <tr class="maybe_disabled disabled">
97
+ <th>
98
+ <label for="compress">
99
+ <?php esc_html_e( 'Use GZ compression', 'search-and-replace' ); ?>
100
+ </label>
101
+ </th>
102
+ <td>
103
+ <input id="compress" type="checkbox" name="compress" disabled />
104
+ </td>
105
+ </tr>
106
+
107
+ </tbody>
108
+ </table>
109
+ <?php $this->show_submit_button( 'search-submit' ); ?>
110
+ </form>
inc/templates/search_replace.php DELETED
@@ -1,52 +0,0 @@
1
- <?php
2
- /**
3
- * Template for displaying search & replace page
4
- */
5
- // Prevent direct access.
6
- if ( ! defined( 'SEARCH_REPLACE_BASEDIR' ) ) {
7
- echo "Hi there! I'm just a part of plugin, not much I can do when called directly.";
8
- exit;
9
- }
10
- ?>
11
- <form action="" method="post">
12
- <table class="form-table">
13
- <tbody>
14
- <tr>
15
- <th><label for="search"><strong><?php esc_html_e( 'Search for: ', 'search-and-replace' ); ?></strong></label></th>
16
- <td><input id="search" type="text" name="search" value="<?php $this->get_search_value() ?>" /></td>
17
- </tr>
18
- <tr>
19
- <th><label for="replace"><strong><?php esc_html_e( 'Replace with: ', 'search-and-replace' ); ?></strong></label></th>
20
- <td><input id="replace" type="text" name="replace" value="<?php $this->get_replace_value() ?>" /></td>
21
- </tr>
22
- <tr>
23
- <th><strong><?php esc_html_e( 'Select tables', 'search-and-replace' ); ?></strong></th>
24
- <td><?php $this->show_table_list(); ?><br>
25
- <br><input id="select_all_tables" type="checkbox" name="select_all" />
26
- <label for="select_all_tables">
27
- <?php esc_html_e( 'Select all tables', 'search-and-replace' ) ?>
28
- </label>
29
- </td>
30
- </tr>
31
-
32
- <tr>
33
- <th><label for="dry_run"><strong><?php esc_html_e( 'Dry Run', 'search-and-replace' ); ?></strong></label></th>
34
- <td><input type="checkbox" id="dry_run" name="dry_run" checked /></td>
35
- </tr>
36
- <tr class="maybe_disabled disabled">
37
- <th><?php esc_html_e( 'Export SQL file or write changes to DB?', 'search-and-replace' ) ?></th>
38
- <td><input id="radio1" type="radio" name="export_or_save" value="export" checked disabled />
39
- <label for="radio1"><?php esc_html_e( 'Export SQL file with changes', 'search-and-replace' ) ?></label>
40
- <br><input id="radio2" type="radio" name="export_or_save" value="save_to_db" disabled />
41
- <label for="radio2"><?php esc_html_e( 'Save changes to Database', 'search-and-replace' ) ?></label>
42
- </td>
43
- </tr>
44
- <tr class="maybe_disabled disabled">
45
- <th><label for="compress"><strong><?php esc_html_e( 'Use GZ compression', 'search-and-replace' ); ?></strong></label></th>
46
- <td><input id="compress" type="checkbox" name="compress" disabled /></td>
47
- </tr>
48
-
49
- </tbody>
50
- </table>
51
- <?php $this->show_submit_button(); ?>
52
- </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/templates/sql-import.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template for displaying sql import page
4
+ */
5
+
6
+ // Prevent direct access.
7
+ if ( ! defined( 'SEARCH_REPLACE_BASEDIR' ) ) {
8
+ echo "Hi there! I'm just a part of plugin, not much I can do when called directly.";
9
+ exit;
10
+ }
11
+ ?>
12
+
13
+ <form action="" method="post" enctype="multipart/form-data">
14
+ <table class="form-table">
15
+ <tbody>
16
+ <tr>
17
+ <th>
18
+ <strong>
19
+ <?php esc_html_e( 'Select SQL file to upload. ', 'search-and-replace' ); ?>
20
+ </strong>
21
+ </th>
22
+
23
+ <td><input type="file" name="file_to_upload" id="file_to_upload"></td>
24
+ </tr>
25
+ <tr>
26
+ <th></th>
27
+ <td>
28
+ <?php esc_html_e( 'Maximum file size: ', 'search-and-replace' ); ?>
29
+ <?php echo floatval( $this->file_upload_max_size() ) . 'KB'; ?>
30
+ </td>
31
+ </tr>
32
+ </tbody>
33
+ </table>
34
+ <?php $this->show_submit_button(); ?>
35
+ </form>
inc/templates/sql_import.php DELETED
@@ -1,24 +0,0 @@
1
- <?php
2
- /**
3
- * Template for displaying sql import page
4
- */
5
- // Prevent direct access.
6
- if ( ! defined( 'SEARCH_REPLACE_BASEDIR' ) ) {
7
- echo "Hi there! I'm just a part of plugin, not much I can do when called directly.";
8
- exit;
9
- }
10
- ?>
11
-
12
- <form action="" method="post" enctype="multipart/form-data">
13
- <table class="form-table">
14
- <tbody>
15
- <tr>
16
- <th><strong><?php esc_html_e( 'Select SQL file to upload. ', 'search-and-replace' ); ?></strong></th>
17
-
18
- <td><input type="file" name="file_to_upload" id="file_to_upload"></td>
19
- </tr>
20
- <tr><th></th><td><?php esc_html_e( 'Maximum file size: ', 'search-and-replace' );echo $this->file_upload_max_size().'KB'; ?></td></tr>
21
- </tbody>
22
- </table>
23
- <?php $this->show_submit_button(); ?>
24
- </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inpsyde-search-replace.php CHANGED
@@ -1,26 +1,32 @@
1
  <?php # -*- coding: utf-8 -*-
 
2
  /**
3
  * Plugin Name: Search & Replace
4
  * Plugin URI: https://wordpress.org/plugins/search-and-replace/
5
  * Description: Search & Replace data in your whole WordPress setup, backup and import your database, change table prefix or migrate your domain to another domain.
6
  * Author: Inpsyde GmbH
7
- * Author URI: http://inpsyde.com
8
  * Contributors: s-hinse, derpixler, ChriCo, Bueltge, inpsyde
9
- * Version: 3.1.2
10
  * Text Domain: search-and-replace
11
  * Domain Path: /languages
12
  * License: GPLv3+
13
  * License URI: license.txt
14
  */
15
 
 
 
 
 
16
 
17
- defined( 'ABSPATH' ) or die( 'No direct access!' );
 
18
 
19
  /**
20
  * Validate requirements on activation
21
  *
22
  * Runs on plugin activation.
23
- * Check if php min 5.4.0 if not deactivate the plugin.
24
  *
25
  * @since 3.1.1
26
  *
@@ -28,10 +34,8 @@ defined( 'ABSPATH' ) or die( 'No direct access!' );
28
  */
29
  function search_replace_activate() {
30
 
31
- global $l10n, $l10n_unloaded;
32
-
33
- $required_php_version = '5.4.0';
34
- $correct_php_version = version_compare( phpversion(), $required_php_version, '>=' );
35
 
36
  search_replace_textdomain();
37
 
@@ -41,49 +45,85 @@ function search_replace_activate() {
41
  wp_die(
42
  '<p>' .
43
  sprintf(
44
- esc_attr__( 'This plugin can not be activated because it requires at least PHP version %1$s. ', 'search-and-replace' ),
 
 
 
 
45
  $required_php_version
46
  )
47
- . '</p> <a href="' . admin_url( 'plugins.php' ) . '">' . esc_attr__( 'back', 'search-and-replace' ) . '</a>'
 
48
  );
49
 
50
  }
51
 
52
  }
53
 
54
- register_activation_hook( __FILE__, 'search_replace_activate' );
55
-
56
-
57
  /**
58
  * Load the plugin
59
  *
60
  * @since 3.1.1
61
  *
62
- * @return void
63
  */
64
- function search_replace_load(){
 
 
 
 
 
 
 
65
 
66
- $load = __DIR__ . '/inc/Load.php';
67
 
68
- if ( file_exists( $load ) ) {
69
- require_once $load;
70
 
71
- define( 'SEARCH_REPLACE_BASEDIR', plugin_dir_url( __FILE__ ) );
 
72
 
73
- $load = new \Inpsyde\SearchReplace\Load();
74
- $load->init();
75
  }
76
 
77
- }
 
78
 
79
- add_action( 'plugins_loaded', 'search_replace_load' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
  /**
82
- * Load plugins translations
83
  */
84
- function search_replace_textdomain(){
85
-
86
- $lang_dir = plugin_basename( __DIR__ ) . '/l10n/';
87
- load_plugin_textdomain( 'search-and-replace', FALSE, $lang_dir );
88
 
89
- }
 
 
 
 
 
1
  <?php # -*- coding: utf-8 -*-
2
+
3
  /**
4
  * Plugin Name: Search & Replace
5
  * Plugin URI: https://wordpress.org/plugins/search-and-replace/
6
  * Description: Search & Replace data in your whole WordPress setup, backup and import your database, change table prefix or migrate your domain to another domain.
7
  * Author: Inpsyde GmbH
8
+ * URI: https://inpsyde.com
9
  * Contributors: s-hinse, derpixler, ChriCo, Bueltge, inpsyde
10
+ * Version: 3.2.0
11
  * Text Domain: search-and-replace
12
  * Domain Path: /languages
13
  * License: GPLv3+
14
  * License URI: license.txt
15
  */
16
 
17
+ use Inpsyde\SearchReplace\Database;
18
+ use Inpsyde\SearchReplace\Page;
19
+
20
+ defined( 'ABSPATH' ) || die( 'No direct access!' );
21
 
22
+ add_action( 'plugins_loaded', 'search_replace_load' );
23
+ register_activation_hook( __FILE__, 'search_replace_activate' );
24
 
25
  /**
26
  * Validate requirements on activation
27
  *
28
  * Runs on plugin activation.
29
+ * Check if php min 5.6.0 if not deactivate the plugin.
30
  *
31
  * @since 3.1.1
32
  *
34
  */
35
  function search_replace_activate() {
36
 
37
+ $required_php_version = '5.6.0';
38
+ $correct_php_version = version_compare( PHP_VERSION, $required_php_version, '>=' );
 
 
39
 
40
  search_replace_textdomain();
41
 
45
  wp_die(
46
  '<p>' .
47
  sprintf(
48
+ // translators: %1$s will replace with the PHP version of the client.
49
+ esc_attr__(
50
+ 'This plugin can not be activated because it requires at least PHP version %1$s. ',
51
+ 'search-and-replace'
52
+ ),
53
  $required_php_version
54
  )
55
+ . '</p> <a href="' . admin_url( 'plugins.php' ) . '">'
56
+ . esc_attr__( 'back', 'search-and-replace' ) . '</a>'
57
  );
58
 
59
  }
60
 
61
  }
62
 
 
 
 
63
  /**
64
  * Load the plugin
65
  *
66
  * @since 3.1.1
67
  *
68
+ * @return bool
69
  */
70
+ function search_replace_load() {
71
+
72
+ global $wpdb;
73
+
74
+ // all hooks are just available on backend.
75
+ if ( ! is_admin() ) {
76
+ return false;
77
+ }
78
 
79
+ define( 'SEARCH_REPLACE_BASEDIR', plugin_dir_url( __FILE__ ) );
80
 
81
+ search_replace_textdomain();
 
82
 
83
+ $user_cap = apply_filters( 'search_replace_access_capability', 'manage_options' );
84
+ $file = __DIR__ . '/vendor/autoload.php';
85
 
86
+ if ( ! current_user_can( $user_cap ) || ! file_exists( $file ) ) {
87
+ return false;
88
  }
89
 
90
+ /** @noinspection PhpIncludeInspection */
91
+ include_once $file;
92
 
93
+ $max_execution = new Inpsyde\SearchReplace\Service\MaxExecutionTime();
94
+
95
+ $dbm = new Database\Manager( $wpdb );
96
+ $replace = new Database\Replace( $dbm, $max_execution );
97
+ $dbe = new Database\Exporter( $replace, $dbm, new \WP_Error() );
98
+ $dbi = new Database\Importer( $max_execution );
99
+
100
+ $downloader = new Inpsyde\SearchReplace\FileDownloader( $dbe, $max_execution );
101
+ add_action( 'init', [ $downloader, 'deliver_backup_file' ] );
102
+
103
+ $page_manager = new Page\Manager();
104
+ $page_manager->add_page( new Page\BackupDatabase( $dbe, $downloader ) );
105
+ $page_manager->add_page( new Page\SearchReplace( $dbm, $replace, $dbe, $downloader ) );
106
+ $page_manager->add_page( new Page\ReplaceDomain( $dbm, $dbe, $downloader ) );
107
+ $page_manager->add_page( new Page\SqlImport( $dbi ) );
108
+ $page_manager->add_page( new Page\Credits() );
109
+
110
+ add_action( 'admin_menu', [ $page_manager, 'register_pages' ] );
111
+ add_action( 'admin_head', [ $page_manager, 'remove_submenu_pages' ] );
112
+
113
+ add_action( 'admin_enqueue_scripts', [ $page_manager, 'register_css' ] );
114
+ add_action( 'admin_enqueue_scripts', [ $page_manager, 'register_js' ] );
115
+
116
+ return true;
117
+ }
118
 
119
  /**
120
+ * Loading the plugin translations.
121
  */
122
+ function search_replace_textdomain() {
 
 
 
123
 
124
+ return load_plugin_textdomain(
125
+ 'search-and-replace',
126
+ false,
127
+ plugin_basename( __DIR__ ) . '/l10n/'
128
+ );
129
+ }
l10n/search-and-replace-de_DE.mo DELETED
Binary file
l10n/search-and-replace-de_DE.po DELETED
@@ -1,557 +0,0 @@
1
- # WordPress Blank Pot
2
- # Copyright (C) 2014 ...
3
- # This file is distributed under the GNU General Public License v2 or later.
4
- msgid ""
5
- msgstr ""
6
- "Project-Id-Version: Inpsyde Search & Replace\n"
7
- "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/inspyde-search-"
8
- "replace\n"
9
- "POT-Creation-Date: 2016-11-08 16:44+0100\n"
10
- "PO-Revision-Date: \n"
11
- "Last-Translator: \n"
12
- "Language-Team: Inpsyde GmbH <hello@inpsyde.com>\n"
13
- "Language: de_DE\n"
14
- "MIME-Version: 1.0\n"
15
- "Content-Type: text/plain; charset=UTF-8\n"
16
- "Content-Transfer-Encoding: 8bit\n"
17
- "Plural-Forms: nplurals=2; plural=(n != 1);\n"
18
- "X-Textdomain-Support: yesX-Generator: Poedit 1.6.4\n"
19
- "X-Poedit-SourceCharset: UTF-8\n"
20
- "X-Poedit-KeywordsList: __;_e;esc_html_e;esc_html_x:1,2c;esc_html__;"
21
- "esc_attr_e;esc_attr_x:1,2c;esc_attr__;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;"
22
- "_x:1,2c;_n:1,2;_n_noop:1,2;__ngettext:1,2;__ngettext_noop:1,2;_c,_nc:4c,1,2\n"
23
- "X-Poedit-Basepath: ..\n"
24
- "X-Generator: Poedit 1.5.4\n"
25
- "X-Poedit-SearchPath-0: inc\n"
26
- "X-Poedit-SearchPath-1: inspyde-search-replace.php\n"
27
-
28
- #: inc/FileDownloader.php:46 inc/FileDownloader.php:63
29
- #: inc/Page/SearchReplace.php:210
30
- msgid "Search pattern not found."
31
- msgstr "Suchmuster nicht gefunden."
32
-
33
- #: inc/FileDownloader.php:69
34
- msgid "Your SQL file was created!"
35
- msgstr "Die SQL-Datei wurde erzeugt."
36
-
37
- #: inc/FileDownloader.php:76
38
- msgid "Download SQL File"
39
- msgstr "SQL-Datei herunterladen"
40
-
41
- #: inc/FileDownloader.php:111
42
- #, php-format
43
- msgid "%s table was processed."
44
- msgid_plural "%s tables were processed."
45
- msgstr[0] "%s Tabelle wurde verarbeitet."
46
- msgstr[1] "%s Tabellen wurden verarbeitet."
47
-
48
- #: inc/FileDownloader.php:121
49
- #, php-format
50
- msgid "%s cell needs to be updated."
51
- msgid_plural "%s cells need to be updated."
52
- msgstr[0] "%s Eintrag muss geändert werden."
53
- msgstr[1] "%s Einträge müssen geändert werden."
54
-
55
- #: inc/FileDownloader.php:132
56
- msgid "View details"
57
- msgstr "Details"
58
-
59
- #: inc/FileDownloader.php:151
60
- msgid "Table:"
61
- msgstr "Tabelle"
62
-
63
- #: inc/FileDownloader.php:153
64
- msgid "Changes:"
65
- msgstr "Änderungen"
66
-
67
- #: inc/FileDownloader.php:163
68
- msgid "row"
69
- msgstr "Zeile"
70
-
71
- #: inc/FileDownloader.php:166
72
- msgid "column"
73
- msgstr "Spalte"
74
-
75
- #: inc/FileDownloader.php:178
76
- msgid "Old value:"
77
- msgstr "Vorher:"
78
-
79
- #: inc/FileDownloader.php:180
80
- msgid "New value:"
81
- msgstr "Nachher:"
82
-
83
- #: inc/Database/Exporter.php:106
84
- msgid "Could not open the backup file for writing!"
85
- msgstr "Konnte die Backup Datei für Import nicht öffnen!"
86
-
87
- #: inc/Database/Exporter.php:112
88
- msgid "The backup directory is not writable!"
89
- msgstr "Das Sicherungsverzeichnis ist nicht beschreibbar!"
90
-
91
- #: inc/Database/Exporter.php:120
92
- msgid "WordPress MySQL database backup"
93
- msgstr "Wordpress MySQL Datenbank-Backup"
94
-
95
- #: inc/Database/Exporter.php:122
96
- #, php-format
97
- msgid "Generated: %s"
98
- msgstr "Generiert: %s"
99
-
100
- #: inc/Database/Exporter.php:123
101
- #, php-format
102
- msgid "Hostname: %s"
103
- msgstr "Hostname: %s"
104
-
105
- #: inc/Database/Exporter.php:124
106
- #, php-format
107
- msgid "Database: %s"
108
- msgstr "Datenbank: %s"
109
-
110
- #: inc/Database/Exporter.php:128
111
- #, php-format
112
- msgid "Changed table prefix: From %s to %s "
113
- msgstr "Ändere Tabellen-Präfix: von %s zu %s"
114
-
115
- #: inc/Database/Exporter.php:216
116
- #, php-format
117
- msgid "Table: %s"
118
- msgstr "Tabelle: %s"
119
-
120
- #: inc/Database/Exporter.php:220
121
- msgid "Error getting table details"
122
- msgstr "Fehler: Keine Tabellendetails gefunden"
123
-
124
- #: inc/Database/Exporter.php:229
125
- #, php-format
126
- msgid "Delete any existing table %s"
127
- msgstr "Lösche existierende Tabelle %s"
128
-
129
- #: inc/Database/Exporter.php:243
130
- #, php-format
131
- msgid "Table structure of table %s"
132
- msgstr "Tabellenstruktur von Tabelle %s"
133
-
134
- #: inc/Database/Exporter.php:253
135
- #, php-format
136
- msgid "Error with SHOW CREATE TABLE for %s."
137
- msgstr "Fehler bei SHOW CREATE TABLE für %s."
138
-
139
- #: inc/Database/Exporter.php:266
140
- #, php-format
141
- msgid "Error getting table structure of %s"
142
- msgstr "Fehler beim Auslesen der Tabellenstruktur von %s"
143
-
144
- #: inc/Database/Exporter.php:276
145
- #, php-format
146
- msgid "Data contents of table %s"
147
- msgstr "Daten von Tabelle %s"
148
-
149
- #: inc/Database/Exporter.php:378
150
- #, php-format
151
- msgid "End of data contents of table %s"
152
- msgstr "Ende der Daten von Tabelle %s"
153
-
154
- #: inc/Database/Exporter.php:460
155
- msgid "There was an error writing a line to the backup script:"
156
- msgstr ""
157
- "Es ist ein Fehler in einer Zeile beim Erstellen des Backups aufgetreten:"
158
-
159
- #: inc/Database/Manager.php:71
160
- #, php-format
161
- msgid "(%s KB)"
162
- msgstr "(%s KB)"
163
-
164
- #: inc/Database/Replace.php:76
165
- msgid "Search and replace pattern can't be the same!"
166
- msgstr "Das Such & Ersetzen Muster darf nicht gleich sein!"
167
-
168
- #: inc/Database/Replace.php:219
169
- #, php-format
170
- msgid "Error updating row: %d."
171
- msgstr "Importieren für Zeile fehlgeschlagen: %d."
172
-
173
- #: inc/Page/AbstractPage.php:26 inc/Page/Manager.php:104
174
- #: inc/Page/SearchReplace.php:64
175
- msgid "Search & Replace"
176
- msgstr "Suchen & Ersetzen"
177
-
178
- #: inc/Page/AbstractPage.php:52
179
- msgid "Errors:"
180
- msgstr "Fehler:"
181
-
182
- #: inc/Page/AbstractPage.php:78
183
- msgid "Submit"
184
- msgstr "Absenden"
185
-
186
- #: inc/Page/BackupDatabase.php:41
187
- msgid "Backup Database"
188
- msgstr "Datenbank-Backup"
189
-
190
- #: inc/Page/BackupDatabase.php:67
191
- msgid "Create SQL File"
192
- msgstr "SQL-Datei erzeugen"
193
-
194
- #: inc/Page/Credits.php:24
195
- msgid "Credits"
196
- msgstr "Herausgeber"
197
-
198
- #: inc/Page/ReplaceDomain.php:55
199
- msgid "Replace Field should not be empty."
200
- msgstr "Das Feld \"Ersetze mit\" sollte nicht leer sein."
201
-
202
- #: inc/Page/ReplaceDomain.php:79
203
- msgid "Do Replace Domain/Url"
204
- msgstr "Jetzt Domain/URL ersetzen"
205
-
206
- #: inc/Page/ReplaceDomain.php:87
207
- msgid "Replace Domain URL"
208
- msgstr "Domain URL ersetzen"
209
-
210
- #: inc/Page/SearchReplace.php:163
211
- msgid "Do Search & Replace"
212
- msgstr "Suche und Ersetze"
213
-
214
- #: inc/Page/SearchReplace.php:182
215
- msgid ""
216
- "Dry run is selected. No changes were made to the database and no SQL file "
217
- "was written ."
218
- msgstr ""
219
- "Testlauf ist ausgewählt. Es wurden keine Änderungen an der Datenbank "
220
- "vorgenommen und keine SQL-Datei geschrieben."
221
-
222
- #: inc/Page/SearchReplace.php:190
223
- msgid "The following changes were made to the database: "
224
- msgstr "Folgende Änderungen wurden in die Datenbank geschrieben: "
225
-
226
- #: inc/Page/SearchReplace.php:227
227
- msgid "No Tables were selected."
228
- msgstr "Es wurden keine Tabellen ausgewählt."
229
-
230
- #: inc/Page/SearchReplace.php:237
231
- msgid "Search field is empty."
232
- msgstr "\"Suche nach\"-Feld ist leer."
233
-
234
- #: inc/Page/SearchReplace.php:249
235
- msgid ""
236
- "Your search contains your current site url. Replacing your site url will "
237
- "most likely cause your site to break. If you want to change the URL (and you "
238
- "know what you doing), please use the export function and make sure you "
239
- "backup your database before reimporting the changed SQL."
240
- msgstr ""
241
- "Dein Suchbegriff enthält die aktuelle URL deiner Seite. Wenn du deine Seiten-"
242
- "URL ersetzt, funktioniert deine Seite wahrscheinlich nicht mehr. Wenn du die "
243
- "URL trotzdem ersetzen willst (und du weißt, was du tust), benutze bitte die "
244
- "Exportfunktion. Mache unbedingt ein Backup deiner Datenbank, bevor du das "
245
- "geänderte SQL importierst."
246
-
247
- #: inc/Page/SqlImport.php:34
248
- msgid "SQL Import"
249
- msgstr "SQL Import"
250
-
251
- #: inc/Page/SqlImport.php:60
252
- msgid "Import SQL file"
253
- msgstr "SQL-Datei importieren"
254
-
255
- #: inc/Page/SqlImport.php:84
256
- msgid "The file has neither '.gz' nor '.sql' Extension. Import not possible."
257
- msgstr ""
258
- "Die Datei hat weder die Erweiterung '.gz' noch '.sql'. Import nicht möglich."
259
-
260
- #: inc/Page/SqlImport.php:97
261
- msgid "The file does not seem to be a valid SQL file. Import not possible."
262
- msgstr ""
263
- "Diese Datei ist anscheinend keine gültige SQL-Datei. Import nicht möglich."
264
-
265
- #: inc/Page/SqlImport.php:106
266
- #, php-format
267
- msgid "The SQL file was successfully imported. %s SQL queries were performed."
268
- msgstr ""
269
- "Die SQL-Datei wurde erfolgreich importiert %s SQL-Abfragen wurden ausgeführt."
270
-
271
- #: inc/Page/SqlImport.php:118
272
- msgid "The uploaded file exceeds the upload_max_filesize directive in php.ini"
273
- msgstr ""
274
- "Die Datei überschreitet die maximale Dateigröße (upload_max_filesize) in der "
275
- "php.ini."
276
-
277
- #: inc/Page/SqlImport.php:121
278
- msgid ""
279
- "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in "
280
- "the HTML form"
281
- msgstr ""
282
- "Die hochgeladene Datei übersteigt die MAX_FILE_SIZE Vorgabe, welches im "
283
- "Formular angegeben wurde."
284
-
285
- #: inc/Page/SqlImport.php:124
286
- msgid "The uploaded file was only partially uploaded"
287
- msgstr "Die Datei wurde leider nur teilweise hochgeladen."
288
-
289
- #: inc/Page/SqlImport.php:125
290
- msgid "No file was uploaded."
291
- msgstr "Es wurde keine Datei hochgeladen."
292
-
293
- #: inc/Page/SqlImport.php:126
294
- msgid "Missing a temporary folder."
295
- msgstr "Temporärer Ordner fehlt."
296
-
297
- #: inc/Page/SqlImport.php:127
298
- msgid "Failed to write file to disk."
299
- msgstr "Fehler beim Schreiben der Datei."
300
-
301
- #: inc/Page/SqlImport.php:128
302
- msgid "A PHP extension stopped the file upload."
303
- msgstr "Eine PHP Erweiterung hat den Dateiupload blockiert."
304
-
305
- #: inc/Page/SqlImport.php:132
306
- msgid "Upload Error: "
307
- msgstr "Fehler beim Upload: "
308
-
309
- #: inc/templates/credits.php:12
310
- msgid "Hey nice to have you here!"
311
- msgstr "Schön, dass du hier bist!"
312
-
313
- #: inc/templates/credits.php:14
314
- #, php-format
315
- msgid ""
316
- "Search and Replace is refactored in 2015 by <a href=\"%1$s\">Inpsyde GmbH</"
317
- "a>, maintained since 2006 and based on the original from <a href=\"%2$s"
318
- "\">Mark Cunningham</a>."
319
- msgstr ""
320
- "Das Plugin Search & Replace wurde 2015 von <a href=\"%1$s\">Inpsyde GmbH</a> "
321
- "überarbeitet, gewartet seit 2006 und basiert auf dem Original von <a href="
322
- "\"%2$s\">Mark Cunningham</a> ."
323
-
324
- #: inc/templates/credits.php:19
325
- msgid "You rock! contribute the plugin."
326
- msgstr "Du bist super! Unterstütze dieses Plugin."
327
-
328
- #: inc/templates/credits.php:21
329
- #, php-format
330
- msgid ""
331
- "You can contribute the Plugin go to the repository on <a href=\"%s\">github</"
332
- "a> making changes, create issues or submitting changes."
333
- msgstr ""
334
- "Du kannst an diesem Plugin mitarbeiten. Du findest unser Repository auf <a "
335
- "href=\"%s\">github</a>. Schlage Änderungen vor, melde Fehler oder löse "
336
- "offene Issues."
337
-
338
- #: inc/templates/credits.php:25
339
- msgid "We are Inpsyde"
340
- msgstr "Wir sind Inpsyde"
341
-
342
- #: inc/templates/credits.php:26
343
- msgid ""
344
- "Inpsyde has developed enterprise solutions with the world’s most popular "
345
- "open-source CMS since it was a kitten. Still do, inconvincible convinced."
346
- msgstr ""
347
- "Inpsyde entwickelt Enterprise Lösungen mit dem weltweit populärstem open-"
348
- "source CMS seit es in den Kinderschuhen steckt. Unbelehrbar überzeugt."
349
-
350
- #: inc/templates/credits.php:28
351
- #, php-format
352
- msgid ""
353
- "Inpsyde is a WordPress <a href=\"%1$s\">VIP Service Partner</a> and <a href="
354
- "\"%2$s\">WooCommerce Expert</a>."
355
- msgstr ""
356
- "Inpsyde ist ein WordPress <a href=\"%1$s\">VIP Service Partner</a> und <a "
357
- "href=\"%2$s\">WooCommerce Expert</a>."
358
-
359
- #: inc/templates/credits.php:33
360
- #, php-format
361
- msgid "Look at our other <a href=\"%s\">free WordPress plugins</a>."
362
- msgstr ""
363
- "Schau dir auch unsere anderen <a href=\"%s\">kostenlosen WordPress plugins</"
364
- "a> an."
365
-
366
- #: inc/templates/credits.php:38
367
- msgid "Working at Inpsyde"
368
- msgstr "Arbeiten bei Inpsyde"
369
-
370
- #: inc/templates/credits.php:39
371
- msgid ""
372
- "The biggest WordPress enterprise in Europe we’re dynamically growing and "
373
- "constantly looking for new employees. So do you want to shape WordPress in "
374
- "an interesting and exciting working environment? Here we are!"
375
- msgstr ""
376
- "Als größte WordPress-Agentur Europas wachsen wir dynamisch und suchen "
377
- "regelmäßig Verstärkung. Willst du Open Source mitgestalten, in einem "
378
- "spannenden Arbeitsumfeld?"
379
-
380
- #: inc/templates/credits.php:40
381
- msgid ""
382
- "At the moment we’re looking for developers for WordPress based products and "
383
- "services. If you’re not a developer and want to be part of us, we’d be happy "
384
- "to recieve your unsolicited application. At Inpsyde you can expect an open, "
385
- "modern and lively company culture:"
386
- msgstr ""
387
- "Aktuell suchen wir Entwickler_innen für WordPress-basierte Produkte und "
388
- "Services. Aber auch andere Initiativbewerbungen sind gerne gesehen. Bei "
389
- "Inpsyde erwartet Dich eine offene, moderne und lebendige Unternehmenskultur:"
390
-
391
- #: inc/templates/credits.php:42
392
- msgid "challenging and exciting projects"
393
- msgstr "Herausfordernde und spannende Projekte"
394
-
395
- #: inc/templates/credits.php:43
396
- msgid "flexible working hours in remote office"
397
- msgstr "Flexible Arbeitszeiten im Remote-Office"
398
-
399
- #: inc/templates/credits.php:44
400
- msgid "deliberately flat hierarchies and short decision paths"
401
- msgstr "Flache Hierarchien mit kurzen Entscheidungswegen"
402
-
403
- #: inc/templates/credits.php:45
404
- msgid "a wide variety of tasks"
405
- msgstr "Abwechslungsreiche Aufgaben"
406
-
407
- #: inc/templates/credits.php:46
408
- msgid "freedom for personal development and responsible, self-reliant action"
409
- msgstr ""
410
- "Freiräume für die persönliche Gestaltung und eigenverantwortliches Handeln"
411
-
412
- #: inc/templates/credits.php:50
413
- #, php-format
414
- msgid ""
415
- "If you love open source and especially WordPress, if you love to organize "
416
- "your working days by yourself and want to use your pragmatic problem-solving "
417
- "skills and result-oriented work methods: <a href=\"%s\">join our team</a>!"
418
- msgstr ""
419
- "Wenn du WordPress und Open Source lebst, Deinen Arbeitstag gerne frei und "
420
- "selbständig organisierst, lösungs- sowie ergebnisorientiert arbeitest und "
421
- "zuverlässig bist, dann komm zu uns! Schau dir unsere aktuellen Job-Angebote "
422
- "an und sende Deine Bewerbung über <a href=\"%s\">unsere Site</a>."
423
-
424
- #: inc/templates/db_backup.php:13
425
- msgid "Create a backup of your database by clicking \"Create SQL File\"."
426
- msgstr ""
427
- "Erstelle ein Backup deiner Datenbank, indem du auf \"SQL-Datei erzeugen\" "
428
- "klickst."
429
-
430
- #: inc/templates/replace_domain.php:11
431
- msgid ""
432
- "If you want to migrate your site to another domain, enter the new URL in the "
433
- "field \"Replace with\" and create a backup of your database by clicking \"Do "
434
- "Replace Domain/Url\"."
435
- msgstr ""
436
- "Wenn du deine Website zu einer anderen Domain umziehen willst, gib die neue "
437
- "URL in das Feld \"Ersetzen durch\" ein und erstelle ein Backup deiner "
438
- "Datenbank durch Klick auf \"Jetzt Domain/URL ersetzen\"."
439
-
440
- #: inc/templates/replace_domain.php:20 inc/templates/search_replace.php:15
441
- msgid "Search for: "
442
- msgstr "Suchen nach: "
443
-
444
- #: inc/templates/replace_domain.php:24 inc/templates/search_replace.php:19
445
- msgid "Replace with: "
446
- msgstr "Ersetzen durch: "
447
-
448
- #: inc/templates/replace_domain.php:25
449
- msgid "New URL"
450
- msgstr "Neue URL"
451
-
452
- #: inc/templates/replace_domain.php:28
453
- msgid "Change database prefix"
454
- msgstr "Datenbank-Präfix ändern"
455
-
456
- #: inc/templates/replace_domain.php:32
457
- msgid "Current prefix: "
458
- msgstr "Aktuelles Prefix:"
459
-
460
- #: inc/templates/replace_domain.php:36
461
- msgid "New prefix: "
462
- msgstr "Neues Prefix:"
463
-
464
- #: inc/templates/replace_domain.php:37
465
- msgid "New database prefix"
466
- msgstr "Neues Datenbank-Präfix"
467
-
468
- #: inc/templates/search_replace.php:23
469
- msgid "Select tables"
470
- msgstr "Tabellen auswählen"
471
-
472
- #: inc/templates/search_replace.php:27
473
- msgid "Select all tables"
474
- msgstr "Alle Tabellen auswählen"
475
-
476
- #: inc/templates/search_replace.php:33
477
- msgid "Dry Run"
478
- msgstr "Testlauf"
479
-
480
- #: inc/templates/search_replace.php:37
481
- msgid "Export SQL file or write changes to DB?"
482
- msgstr ""
483
- "Eine SQL-Datei exportieren oder die Änderungen in die Datenbank schreiben?"
484
-
485
- #: inc/templates/search_replace.php:39
486
- msgid "Export SQL file with changes"
487
- msgstr "SQL-Datei mit Änderungen exportieren"
488
-
489
- #: inc/templates/search_replace.php:41
490
- msgid "Save changes to Database"
491
- msgstr "Änderungen in die Datenbank schreiben"
492
-
493
- #: inc/templates/search_replace.php:45
494
- msgid "Use GZ compression"
495
- msgstr "GZ-Kompression benutzen"
496
-
497
- #: inc/templates/sql_import.php:16
498
- msgid "Select SQL file to upload. "
499
- msgstr "SQL-Datei für den Upload auswählen. "
500
-
501
- #: inc/templates/sql_import.php:20
502
- msgid "Maximum file size: "
503
- msgstr "Maximale Dateigröße: "
504
-
505
- #~ msgid ""
506
- #~ "This plugin can not be activated because it requires at least PHP version "
507
- #~ "%1$s. "
508
- #~ msgstr ""
509
- #~ "Das Plugin kann nicht aktiviert werden, da es mindestens PHP-Version %1$s "
510
- #~ "erfordert. "
511
-
512
- #~ msgid "back"
513
- #~ msgstr "zurück"
514
-
515
- #~ msgid "https://wordpress.org/plugins/search-and-replace/"
516
- #~ msgstr "https://wordpress.org/plugins/search-and-replace/"
517
-
518
- #~ msgid ""
519
- #~ "Search & Replace data in your whole WordPress setup, backup and import "
520
- #~ "your database, change table prefix or migrate your domain to another "
521
- #~ "domain."
522
- #~ msgstr ""
523
- #~ "Suche & Ersetze Daten in deinem kompletten WordPress Umfeld, Backup und "
524
- #~ "Import deiner Datenbank, ändere den Tabellen-Prefix oder Migriere deine "
525
- #~ "Domain in eine andere Domain."
526
-
527
- #~ msgid "Inpsyde GmbH"
528
- #~ msgstr "Inpsyde GmbH"
529
-
530
- #~ msgid "http://inpsyde.com"
531
- #~ msgstr "http://inpsyde.com"
532
-
533
- #~ msgid "<b>Mysqli Error:</b> "
534
- #~ msgstr "<b>Mysqli Fehler:</b> "
535
-
536
- #, fuzzy
537
- #~ msgid "Search & Replace Page"
538
- #~ msgstr "Suchen und Ersetzen"
539
-
540
- #~ msgid "Replace Domain/URL"
541
- #~ msgstr "Domain URL ersetzen"
542
-
543
- #~ msgid "Search and Replace"
544
- #~ msgstr "Suchen und Ersetzen"
545
-
546
- #~ msgid "Inpsyde Search & Replace"
547
- #~ msgstr "Suchen & Ersetzen"
548
-
549
- #~ msgid "Inpsyde Search Replace"
550
- #~ msgstr "Inpsyde Suchen und Ersetzen"
551
-
552
- #~ msgid ""
553
- #~ "<p><strong>%d</strong> tables were processed, <strong>%d</strong> cells "
554
- #~ "were found that need to be updated.</p>"
555
- #~ msgstr ""
556
- #~ "<p><strong>%d</strong> Tabellen wurden verarbeitet, es wurden <strong>%d</"
557
- #~ "strong> Zellen gefunden, die aktualisiert werden müssen.</p>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
l10n/search-and-replace-zh_CN.mo DELETED
Binary file
l10n/search-and-replace-zh_CN.po DELETED
@@ -1,489 +0,0 @@
1
- # Copyright (C) 2016 Inpsyde GmbH
2
- # This file is distributed under the GPLv3+.
3
- msgid ""
4
- msgstr ""
5
- "Project-Id-Version: Search & Replace 3.0.1\n"
6
- "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/inspyde-search-"
7
- "replace\n"
8
- "POT-Creation-Date: 2016-04-07 08:45+0200\n"
9
- "PO-Revision-Date: 2016-04-07 08:45+0200\n"
10
- "Last-Translator: Frank Bültge <frank@bueltge.de>\n"
11
- "Language-Team: Vincent Liou <ljxprime@foxmail.com>\n"
12
- "Language: zh_CN\n"
13
- "MIME-Version: 1.0\n"
14
- "Content-Type: text/plain; charset=UTF-8\n"
15
- "Content-Transfer-Encoding: 8bit\n"
16
- "X-Generator: Poedit 1.8.4\n"
17
- "X-Poedit-KeywordsList: __;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;"
18
- "_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;"
19
- "esc_html_x:1,2c\n"
20
- "Poedit: \n"
21
- "Plural-Forms: nplurals=1; plural=0;\n"
22
-
23
- #: inc/Database/Exporter.php:106
24
- msgid "Could not open the backup file for writing!"
25
- msgstr "备份文件无法写入!"
26
-
27
- #: inc/Database/Exporter.php:112
28
- msgid "The backup directory is not writable!"
29
- msgstr "备份目录不可写!"
30
-
31
- #: inc/Database/Exporter.php:120
32
- msgid "WordPress MySQL database backup"
33
- msgstr "WordPress MySQL 数据库备份"
34
-
35
- #: inc/Database/Exporter.php:122
36
- msgid "Generated: %s"
37
- msgstr "已生成:%s"
38
-
39
- #: inc/Database/Exporter.php:123
40
- msgid "Hostname: %s"
41
- msgstr "主机名:%s"
42
-
43
- #: inc/Database/Exporter.php:124
44
- msgid "Database: %s"
45
- msgstr "数据库:%s"
46
-
47
- #: inc/Database/Exporter.php:128
48
- msgid "Changed table prefix: From %s to %s "
49
- msgstr "表前缀已修改:原为 %s,现为 %s "
50
-
51
- #: inc/Database/Exporter.php:216
52
- msgid "Table: %s"
53
- msgstr "表:%s"
54
-
55
- #: inc/Database/Exporter.php:220
56
- msgid "Error getting table details"
57
- msgstr "获取表详情时发生错误"
58
-
59
- #: inc/Database/Exporter.php:229
60
- msgid "Delete any existing table %s"
61
- msgstr "删除所有存在的表 %s"
62
-
63
- #: inc/Database/Exporter.php:243
64
- msgid "Table structure of table %s"
65
- msgstr "%s 表的结构"
66
-
67
- #: inc/Database/Exporter.php:253
68
- msgid "Error with SHOW CREATE TABLE for %s."
69
- msgstr "对 %s 执行 SHOW CREATE TABLE 语句时发生错误。"
70
-
71
- #: inc/Database/Exporter.php:266
72
- msgid "Error getting table structure of %s"
73
- msgstr "获取 %s 表的结构时发生错误"
74
-
75
- #: inc/Database/Exporter.php:276
76
- msgid "Data contents of table %s"
77
- msgstr "%s 表的数据内容"
78
-
79
- #: inc/Database/Exporter.php:378
80
- msgid "End of data contents of table %s"
81
- msgstr "%s 表的数据结尾"
82
-
83
- #: inc/Database/Exporter.php:460
84
- msgid "There was an error writing a line to the backup script:"
85
- msgstr "向备份脚本中写入如下语句时发生错误:"
86
-
87
- #: inc/Database/Manager.php:71
88
- msgid "(%s KB)"
89
- msgstr "(%s KB)"
90
-
91
- #: inc/Database/Replace.php:76
92
- msgid "Search and replace pattern can't be the same!"
93
- msgstr ""
94
-
95
- #: inc/Database/Replace.php:218
96
- msgid "Error updating row: %d."
97
- msgstr "更新此行时发生错误:%d。"
98
-
99
- #: inc/FileDownloader.php:40 inc/FileDownloader.php:56
100
- #: inc/Page/SearchReplace.php:197
101
- msgid "Search pattern not found."
102
- msgstr "您搜索的表达式无结果。"
103
-
104
- #: inc/FileDownloader.php:62
105
- msgid "Your SQL file was created!"
106
- msgstr "您的 SQL 已生成!"
107
-
108
- #: inc/FileDownloader.php:68
109
- #, fuzzy
110
- msgid "Download SQL File"
111
- msgstr "创建 SQL 文件"
112
-
113
- #: inc/FileDownloader.php:102
114
- msgid "%s table was processed."
115
- msgid_plural "%s tables were processed."
116
- msgstr[0] "%s 个表处理完毕。"
117
-
118
- #: inc/FileDownloader.php:112
119
- msgid "%s cell needs to be updated."
120
- msgid_plural "%s cells need to be updated."
121
- msgstr[0] "有 %s 个单元格需要更新。"
122
-
123
- #: inc/FileDownloader.php:124
124
- msgid "View details"
125
- msgstr "查看详细"
126
-
127
- #: inc/FileDownloader.php:139
128
- msgid "Table:"
129
- msgstr "表:"
130
-
131
- #: inc/FileDownloader.php:140
132
- msgid "Changes:"
133
- msgstr "更改:"
134
-
135
- #: inc/FileDownloader.php:148
136
- msgid "row"
137
- msgstr "行"
138
-
139
- #: inc/FileDownloader.php:150
140
- msgid "column"
141
- msgstr "列"
142
-
143
- #: inc/FileDownloader.php:162
144
- msgid "Old value:"
145
- msgstr "原值:"
146
-
147
- #: inc/FileDownloader.php:164
148
- msgid "New value:"
149
- msgstr "新值:"
150
-
151
- #. Plugin Name of the plugin/theme
152
- msgid "Search & Replace"
153
- msgstr "查找与替换"
154
-
155
- #: inc/Page/AbstractPage.php:52
156
- msgid "Errors:"
157
- msgstr "错误:"
158
-
159
- #: inc/Page/AbstractPage.php:78
160
- msgid "Submit"
161
- msgstr ""
162
-
163
- #: inc/Page/BackupDatabase.php:41
164
- msgid "Backup Database"
165
- msgstr "备份数据库"
166
-
167
- #: inc/Page/BackupDatabase.php:57
168
- msgid "Create SQL File"
169
- msgstr "创建 SQL 文件"
170
-
171
- #: inc/Page/Credits.php:24
172
- msgid "Credits"
173
- msgstr "插件信息"
174
-
175
- #: inc/Page/ReplaceDomain.php:55
176
- msgid "Replace Field should not be empty."
177
- msgstr "替换一栏不应为空。"
178
-
179
- #: inc/Page/ReplaceDomain.php:79
180
- #, fuzzy
181
- msgid "Do Replace Domain/Url"
182
- msgstr "替换域名或 URL"
183
-
184
- #: inc/Page/ReplaceDomain.php:87
185
- msgid "Replace Domain URL"
186
- msgstr "替换域名 URL"
187
-
188
- #: inc/Page/SearchReplace.php:150
189
- msgid "Do Search & Replace"
190
- msgstr "执行替换操作"
191
-
192
- #: inc/Page/SearchReplace.php:168
193
- msgid ""
194
- "Dry run is selected. No changes were made to the database and no SQL file "
195
- "was written ."
196
- msgstr "您选择了预演模式,故此操作不会修改您的数据库或写入 SQL 文件。"
197
-
198
- #: inc/Page/SearchReplace.php:176
199
- msgid "The following changes were made to the database: "
200
- msgstr "程序执行的数据库更改如下: "
201
-
202
- #: inc/Page/SearchReplace.php:214
203
- msgid "No Tables were selected."
204
- msgstr "未选中任何表。"
205
-
206
- #: inc/Page/SearchReplace.php:224
207
- msgid "Search field is empty."
208
- msgstr "搜索一栏没有内容。"
209
-
210
- #: inc/Page/SearchReplace.php:235
211
- msgid ""
212
- "Your search contains your current site url. Replacing your site url will "
213
- "most likely cause your site to break. If you want to change the URL (and you "
214
- "know what you doing), please use the export function and make sure you "
215
- "backup your database before reimporting the changed SQL."
216
- msgstr ""
217
- "您的搜索中包含了当前的站点地址,替换站点地址很可能会导致您的站点出现故障。如"
218
- "果确实想要修改站点的 URL(并且您很清楚自己这么做的后果),请使用导出功能,并"
219
- "确保在重新导入修改后的 SQL 文件时,事先做好备份工作。"
220
-
221
- #: inc/Page/SqlImport.php:34
222
- msgid "SQL Import"
223
- msgstr "SQL 导入"
224
-
225
- #: inc/Page/SqlImport.php:50
226
- msgid "Import SQL file"
227
- msgstr "导入 SQL 文件"
228
-
229
- #: inc/Page/SqlImport.php:73
230
- msgid "The file has neither '.gz' nor '.sql' Extension. Import not possible."
231
- msgstr "请确保文件的扩展名为 .gz 或 .sql,否则无法导入。"
232
-
233
- #: inc/Page/SqlImport.php:86
234
- msgid "The file does not seem to be a valid SQL file. Import not possible."
235
- msgstr "此文件似乎不是有效的 SQL 文件,导入无法进行。"
236
-
237
- #: inc/Page/SqlImport.php:95
238
- msgid "The SQL file was successfully imported. %s SQL queries were performed."
239
- msgstr "SQL 文件导入成功,共执行了 %s 次 SQL 查询。"
240
-
241
- #: inc/Page/SqlImport.php:107
242
- msgid "The uploaded file exceeds the upload_max_filesize directive in php.ini"
243
- msgstr "您所上传的文件大小超出了 php.ini 中 upload_max_filesize 指令的值"
244
-
245
- #: inc/Page/SqlImport.php:110
246
- msgid ""
247
- "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in "
248
- "the HTML form"
249
- msgstr "您所上传的文件大小超出了 HTML 表单中 MAX_FILE_SIZE 指令的值"
250
-
251
- #: inc/Page/SqlImport.php:114
252
- msgid "The uploaded file was only partially uploaded"
253
- msgstr "您所上传的文件只上传了一部分"
254
-
255
- #: inc/Page/SqlImport.php:115
256
- msgid "No file was uploaded."
257
- msgstr "未上传任何文件。"
258
-
259
- #: inc/Page/SqlImport.php:116
260
- msgid "Missing a temporary folder."
261
- msgstr "临时文件夹缺失。"
262
-
263
- #: inc/Page/SqlImport.php:117
264
- msgid "Failed to write file to disk."
265
- msgstr "无法将文件写入磁盘。"
266
-
267
- #: inc/Page/SqlImport.php:118
268
- msgid "A PHP extension stopped the file upload."
269
- msgstr "某 PHP 扩展终止了上传操作。"
270
-
271
- #: inc/templates/credits.php:12
272
- msgid "Hey nice to have you here!"
273
- msgstr "嘿,很高兴在这里看到你!"
274
-
275
- #: inc/templates/credits.php:14
276
- msgid ""
277
- "Search and Replace is refactored in 2015 by <a href=\"%1$s\">Inpsyde GmbH</"
278
- "a>, maintained since 2006 and based on the original from <a href=\"%2$s"
279
- "\">Mark Cunningham</a>."
280
- msgstr ""
281
- "您现在所使用的 Search and Replace 插件是由 <a href=\"%1$s\">Inpsyde GmbH</a> "
282
- "于 2015 年重构的版本,它最初由 <a href=\"%2$s\">Mark Cunningham</a> 于 2006 "
283
- "年编写并维护的。"
284
-
285
- #: inc/templates/credits.php:19
286
- msgid "You rock! contribute the plugin."
287
- msgstr "帅呆啦!一起参与贡献吧"
288
-
289
- #: inc/templates/credits.php:21
290
- msgid ""
291
- "You can contribute the Plugin go to the repository on <a href=\"%s\">github</"
292
- "a> making changes, create issues or submitting changes."
293
- msgstr ""
294
- "你可以在 <a href=\"%s\">Github</a> 参与插件的改进,比如修改代码、创建问题或提"
295
- "交变动。"
296
-
297
- #: inc/templates/credits.php:25
298
- msgid "We are Inpsyde"
299
- msgstr "我们是 Inpsyde"
300
-
301
- #: inc/templates/credits.php:26
302
- msgid ""
303
- "Inpsyde has developed enterprise solutions with the world’s most popular "
304
- "open-source CMS since it was a kitten. Still do, inconvincible convinced."
305
- msgstr ""
306
- "Inpsyde 自诞生之日起就利用这个世界上最流行的开源 CMS 平台研发了许多企业级的解"
307
- "决方案:挑战不可能!"
308
-
309
- #: inc/templates/credits.php:28
310
- msgid ""
311
- "Inpsyde is a WordPress <a href=\"%1$s\">VIP Service Partner</a> and <a href="
312
- "\"%2$s\">WooCommerce Expert</a>."
313
- msgstr ""
314
- "Inpsyde 为 WodPress 的 <a href=\"%1$s\">VIP 服务伙伴</a> 与 WooCommerce <a "
315
- "href=\"%2$s\">专家</a>。"
316
-
317
- #: inc/templates/credits.php:33
318
- msgid "Look at our other <a href=\"%s\">free WordPress plugins</a>."
319
- msgstr ""
320
- "去看看还有哪些 <a href=\"%s\">免费的 WordPress 插件</a> 是由我们开发的。"
321
-
322
- #: inc/templates/credits.php:38
323
- msgid "Working at Inpsyde"
324
- msgstr "在 Inpsyde 工作"
325
-
326
- #: inc/templates/credits.php:39
327
- msgid ""
328
- "The biggest WordPress enterprise in Europe we’re dynamically growing and "
329
- "constantly looking for new employees. So do you want to shape WordPress in "
330
- "an interesting and exciting working environment? Here we are!"
331
- msgstr ""
332
- "作为欧洲最大的 WordPress 企业,我们依旧处在不断的发展之中,并且渴望吸纳更多的"
333
- "有才之人。如果你想要在一个充满激情与乐趣的环境中改造 WordPress,我们在这里等"
334
- "你!"
335
-
336
- #: inc/templates/credits.php:40
337
- msgid ""
338
- "At the moment we’re looking for developers for WordPress based products and "
339
- "services. If you’re not a developer and want to be part of us, we’d be happy "
340
- "to recieve your unsolicited application. At Inpsyde you can expect an open, "
341
- "modern and lively company culture:"
342
- msgstr ""
343
- "目前我们正在招募有关 WordPress 产品服务方面的开发者。如果你不是一名开发者,但"
344
- "仍想成为我们的一员,我们也很乐意收到你主动投来的简历。在 Inpsyde 你将会获得开"
345
- "放、现代与富有生气的企业文化:"
346
-
347
- #: inc/templates/credits.php:42
348
- msgid "challenging and exciting projects"
349
- msgstr "富有激情与挑战性的项目"
350
-
351
- #: inc/templates/credits.php:43
352
- msgid "flexible working hours in remote office"
353
- msgstr "灵活的远程办公时间"
354
-
355
- #: inc/templates/credits.php:44
356
- msgid "deliberately flat hierarchies and short decision paths"
357
- msgstr "平等的地位与迅速的决策路径"
358
-
359
- #: inc/templates/credits.php:45
360
- msgid "a wide variety of tasks"
361
- msgstr "各种各样的任务"
362
-
363
- #: inc/templates/credits.php:46
364
- msgid "freedom for personal development and responsible, self-reliant action"
365
- msgstr "个人发展、责任与自我独立的自由"
366
-
367
- #: inc/templates/credits.php:50
368
- msgid ""
369
- "If you love open source and especially WordPress, if you love to organize "
370
- "your working days by yourself and want to use your pragmatic problem-solving "
371
- "skills and result-oriented work methods: <a href=\"%s\">join our team</a>!"
372
- msgstr ""
373
- "如果你热爱开源事业,尤其是 WordPress,如果你想要自己自己安排自己的工作时间、"
374
- "发挥自己编程解决问题的能力,以及成果导向的工作方式:<a href=\"%s\">加入我们吧"
375
- "</a>!"
376
-
377
- #: inc/templates/db_backup.php:12
378
- msgid "Create a backup of your database by clicking \"Create SQL File\"."
379
- msgstr "点击 创建 SQL 文件 以创建您的数据库备份。"
380
-
381
- #: inc/templates/replace_domain.php:11
382
- #, fuzzy
383
- msgid ""
384
- "If you want to migrate your site to another domain, enter the new URL in the "
385
- "field \"Replace with\" and create a backup of your database by clicking \"Do "
386
- "Replace Domain/Url\"."
387
- msgstr ""
388
- "如果您想要把自己的站点转移到其他的域名之下,请在 替换为 一栏输入新的 URL,并"
389
- "且点击 创建 SQL 文件 以获取您的数据库备份。"
390
-
391
- #: inc/templates/replace_domain.php:20 inc/templates/search_replace.php:15
392
- msgid "Search for: "
393
- msgstr "查找: "
394
-
395
- #: inc/templates/replace_domain.php:24 inc/templates/search_replace.php:19
396
- msgid "Replace with: "
397
- msgstr "替换为: "
398
-
399
- #: inc/templates/replace_domain.php:25
400
- msgid "New URL"
401
- msgstr "新 URL"
402
-
403
- #: inc/templates/replace_domain.php:28
404
- msgid "Change database prefix"
405
- msgstr "更改数据库前缀"
406
-
407
- #: inc/templates/replace_domain.php:32
408
- msgid "Current prefix: "
409
- msgstr "当前前缀: "
410
-
411
- #: inc/templates/replace_domain.php:36
412
- msgid "New prefix: "
413
- msgstr "新前缀: "
414
-
415
- #: inc/templates/replace_domain.php:37
416
- msgid "New database prefix"
417
- msgstr "新数据库前缀"
418
-
419
- #: inc/templates/search_replace.php:23
420
- msgid "Select tables"
421
- msgstr "选择表"
422
-
423
- #: inc/templates/search_replace.php:27
424
- msgid "Select all tables"
425
- msgstr "选择所有表"
426
-
427
- #: inc/templates/search_replace.php:33
428
- msgid "Dry Run"
429
- msgstr "预演模式"
430
-
431
- #: inc/templates/search_replace.php:37
432
- msgid "Export SQL file or write changes to DB?"
433
- msgstr "你想要导出 SQL 文件,还是将更改写入数据库?"
434
-
435
- #: inc/templates/search_replace.php:39
436
- msgid "Export SQL file with changes"
437
- msgstr "将更改导出为 SQL 文件"
438
-
439
- #: inc/templates/search_replace.php:41
440
- msgid "Save changes to Database"
441
- msgstr "将更改写入数据库"
442
-
443
- #: inc/templates/search_replace.php:45
444
- msgid "Use GZ compression"
445
- msgstr "使用 GZ 压缩"
446
-
447
- #: inc/templates/sql_import.php:16
448
- msgid "Select SQL file to upload. "
449
- msgstr "选择需要上传的 SQL 文件 "
450
-
451
- #: inc/templates/sql_import.php:20
452
- msgid "Maximum file size: "
453
- msgstr "最大文件尺寸: "
454
-
455
- #: inspyde-search-replace.php:52
456
- msgid ""
457
- "This plugin can not be activated because it requires at least PHP version "
458
- "%1$s. "
459
- msgstr "此插件无法被激活,因为您的 PHP 版本低于%1$s。 "
460
-
461
- #: inspyde-search-replace.php:55
462
- msgid "back"
463
- msgstr "后退"
464
-
465
- #. Plugin URI of the plugin/theme
466
- msgid "https://wordpress.org/plugins/search-and-replace/"
467
- msgstr "https://wordpress.org/plugins/search-and-replace/"
468
-
469
- #. Description of the plugin/theme
470
- msgid ""
471
- "Search & Replace data in your whole WordPress setup, backup and import your "
472
- "database, change table prefix or migrate your domain to another domain."
473
- msgstr ""
474
- "在您的 WordPress 安装中进行查找和替换操作,同时它还可以实现备份您的数据库、修"
475
- "改表前缀以及域名搬家等诸多功能。"
476
-
477
- #. Author of the plugin/theme
478
- msgid "Inpsyde GmbH"
479
- msgstr "Inpsyde GmbH"
480
-
481
- #. Author URI of the plugin/theme
482
- msgid "http://inpsyde.com"
483
- msgstr "http://inpsyde.com"
484
-
485
- #~ msgid "Search & Replace Page"
486
- #~ msgstr "Search & Replace 选项"
487
-
488
- #~ msgid "Search and Replace"
489
- #~ msgstr "查找并替换"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
l10n/search-and-replace.pot DELETED
@@ -1,455 +0,0 @@
1
- # Copyright (C) 2016 Inpsyde GmbH
2
- # This file is distributed under the GPLv3+.
3
- msgid ""
4
- msgstr ""
5
- "Project-Id-Version: Search & Replace 3.1.0\n"
6
- "Report-Msgid-Bugs-To: "
7
- "https://wordpress.org/support/plugin/inspyde-search-replace\n"
8
- "POT-Creation-Date: 2016-04-07 11:20:16+00:00\n"
9
- "MIME-Version: 1.0\n"
10
- "Content-Type: text/plain; charset=utf-8\n"
11
- "Content-Transfer-Encoding: 8bit\n"
12
- "PO-Revision-Date: 2016-MO-DA HO:MI+ZONE\n"
13
- "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
- "Language-Team: LANGUAGE <LL@li.org>\n"
15
- "X-Generator: grunt-wp-i18n 0.5.3\n"
16
- "X-Poedit-KeywordsList: "
17
- "__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_"
18
- "attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n"
19
- "Poedit: \n"
20
-
21
- #: inc/Database/Exporter.php:106
22
- msgid "Could not open the backup file for writing!"
23
- msgstr ""
24
-
25
- #: inc/Database/Exporter.php:112
26
- msgid "The backup directory is not writable!"
27
- msgstr ""
28
-
29
- #: inc/Database/Exporter.php:120
30
- msgid "WordPress MySQL database backup"
31
- msgstr ""
32
-
33
- #: inc/Database/Exporter.php:122
34
- msgid "Generated: %s"
35
- msgstr ""
36
-
37
- #: inc/Database/Exporter.php:123
38
- msgid "Hostname: %s"
39
- msgstr ""
40
-
41
- #: inc/Database/Exporter.php:124
42
- msgid "Database: %s"
43
- msgstr ""
44
-
45
- #: inc/Database/Exporter.php:128
46
- msgid "Changed table prefix: From %s to %s "
47
- msgstr ""
48
-
49
- #: inc/Database/Exporter.php:216
50
- msgid "Table: %s"
51
- msgstr ""
52
-
53
- #: inc/Database/Exporter.php:220
54
- msgid "Error getting table details"
55
- msgstr ""
56
-
57
- #: inc/Database/Exporter.php:229
58
- msgid "Delete any existing table %s"
59
- msgstr ""
60
-
61
- #: inc/Database/Exporter.php:243
62
- msgid "Table structure of table %s"
63
- msgstr ""
64
-
65
- #: inc/Database/Exporter.php:253
66
- msgid "Error with SHOW CREATE TABLE for %s."
67
- msgstr ""
68
-
69
- #: inc/Database/Exporter.php:266
70
- msgid "Error getting table structure of %s"
71
- msgstr ""
72
-
73
- #: inc/Database/Exporter.php:276
74
- msgid "Data contents of table %s"
75
- msgstr ""
76
-
77
- #: inc/Database/Exporter.php:378
78
- msgid "End of data contents of table %s"
79
- msgstr ""
80
-
81
- #: inc/Database/Exporter.php:460
82
- msgid "There was an error writing a line to the backup script:"
83
- msgstr ""
84
-
85
- #: inc/Database/Manager.php:71
86
- msgid "(%s KB)"
87
- msgstr ""
88
-
89
- #: inc/Database/Replace.php:76
90
- msgid "Search and replace pattern can't be the same!"
91
- msgstr ""
92
-
93
- #: inc/Database/Replace.php:219
94
- msgid "Error updating row: %d."
95
- msgstr ""
96
-
97
- #: inc/FileDownloader.php:46 inc/FileDownloader.php:63
98
- #: inc/Page/SearchReplace.php:207
99
- msgid "Search pattern not found."
100
- msgstr ""
101
-
102
- #: inc/FileDownloader.php:69
103
- msgid "Your SQL file was created!"
104
- msgstr ""
105
-
106
- #: inc/FileDownloader.php:75
107
- msgid "Download SQL File"
108
- msgstr ""
109
-
110
- #: inc/FileDownloader.php:110
111
- msgid "%s table was processed."
112
- msgid_plural "%s tables were processed."
113
- msgstr[0] ""
114
- msgstr[1] ""
115
-
116
- #: inc/FileDownloader.php:120
117
- msgid "%s cell needs to be updated."
118
- msgid_plural "%s cells need to be updated."
119
- msgstr[0] ""
120
- msgstr[1] ""
121
-
122
- #: inc/FileDownloader.php:132
123
- msgid "View details"
124
- msgstr ""
125
-
126
- #: inc/FileDownloader.php:151
127
- msgid "Table:"
128
- msgstr ""
129
-
130
- #: inc/FileDownloader.php:153
131
- msgid "Changes:"
132
- msgstr ""
133
-
134
- #: inc/FileDownloader.php:163
135
- msgid "row"
136
- msgstr ""
137
-
138
- #: inc/FileDownloader.php:166
139
- msgid "column"
140
- msgstr ""
141
-
142
- #: inc/FileDownloader.php:178
143
- msgid "Old value:"
144
- msgstr ""
145
-
146
- #: inc/FileDownloader.php:180
147
- msgid "New value:"
148
- msgstr ""
149
-
150
- #. Plugin Name of the plugin/theme
151
- msgid "Search & Replace"
152
- msgstr ""
153
-
154
- #: inc/Page/AbstractPage.php:52
155
- msgid "Errors:"
156
- msgstr ""
157
-
158
- #: inc/Page/AbstractPage.php:78
159
- msgid "Submit"
160
- msgstr ""
161
-
162
- #: inc/Page/BackupDatabase.php:41
163
- msgid "Backup Database"
164
- msgstr ""
165
-
166
- #: inc/Page/BackupDatabase.php:67
167
- msgid "Create SQL File"
168
- msgstr ""
169
-
170
- #: inc/Page/Credits.php:24
171
- msgid "Credits"
172
- msgstr ""
173
-
174
- #: inc/Page/ReplaceDomain.php:55
175
- msgid "Replace Field should not be empty."
176
- msgstr ""
177
-
178
- #: inc/Page/ReplaceDomain.php:79
179
- msgid "Do Replace Domain/Url"
180
- msgstr ""
181
-
182
- #: inc/Page/ReplaceDomain.php:87
183
- msgid "Replace Domain URL"
184
- msgstr ""
185
-
186
- #: inc/Page/SearchReplace.php:160
187
- msgid "Do Search & Replace"
188
- msgstr ""
189
-
190
- #: inc/Page/SearchReplace.php:178
191
- msgid ""
192
- "Dry run is selected. No changes were made to the database and no SQL file "
193
- "was written ."
194
- msgstr ""
195
-
196
- #: inc/Page/SearchReplace.php:186
197
- msgid "The following changes were made to the database: "
198
- msgstr ""
199
-
200
- #: inc/Page/SearchReplace.php:224
201
- msgid "No Tables were selected."
202
- msgstr ""
203
-
204
- #: inc/Page/SearchReplace.php:234
205
- msgid "Search field is empty."
206
- msgstr ""
207
-
208
- #: inc/Page/SearchReplace.php:245
209
- msgid ""
210
- "Your search contains your current site url. Replacing your site url will "
211
- "most likely cause your site to break. If you want to change the URL (and "
212
- "you know what you doing), please use the export function and make sure you "
213
- "backup your database before reimporting the changed SQL."
214
- msgstr ""
215
-
216
- #: inc/Page/SqlImport.php:34
217
- msgid "SQL Import"
218
- msgstr ""
219
-
220
- #: inc/Page/SqlImport.php:60
221
- msgid "Import SQL file"
222
- msgstr ""
223
-
224
- #: inc/Page/SqlImport.php:83
225
- msgid "The file has neither '.gz' nor '.sql' Extension. Import not possible."
226
- msgstr ""
227
-
228
- #: inc/Page/SqlImport.php:96
229
- msgid "The file does not seem to be a valid SQL file. Import not possible."
230
- msgstr ""
231
-
232
- #: inc/Page/SqlImport.php:105
233
- msgid "The SQL file was successfully imported. %s SQL queries were performed."
234
- msgstr ""
235
-
236
- #: inc/Page/SqlImport.php:117
237
- msgid "The uploaded file exceeds the upload_max_filesize directive in php.ini"
238
- msgstr ""
239
-
240
- #: inc/Page/SqlImport.php:120
241
- msgid ""
242
- "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in "
243
- "the HTML form"
244
- msgstr ""
245
-
246
- #: inc/Page/SqlImport.php:124
247
- msgid "The uploaded file was only partially uploaded"
248
- msgstr ""
249
-
250
- #: inc/Page/SqlImport.php:125
251
- msgid "No file was uploaded."
252
- msgstr ""
253
-
254
- #: inc/Page/SqlImport.php:126
255
- msgid "Missing a temporary folder."
256
- msgstr ""
257
-
258
- #: inc/Page/SqlImport.php:127
259
- msgid "Failed to write file to disk."
260
- msgstr ""
261
-
262
- #: inc/Page/SqlImport.php:128
263
- msgid "A PHP extension stopped the file upload."
264
- msgstr ""
265
-
266
- #: inc/templates/credits.php:12
267
- msgid "Hey nice to have you here!"
268
- msgstr ""
269
-
270
- #: inc/templates/credits.php:14
271
- msgid ""
272
- "Search and Replace is refactored in 2015 by <a href=\"%1$s\">Inpsyde "
273
- "GmbH</a>, maintained since 2006 and based on the original from <a "
274
- "href=\"%2$s\">Mark Cunningham</a>."
275
- msgstr ""
276
-
277
- #: inc/templates/credits.php:19
278
- msgid "You rock! contribute the plugin."
279
- msgstr ""
280
-
281
- #: inc/templates/credits.php:21
282
- msgid ""
283
- "You can contribute the Plugin go to the repository on <a "
284
- "href=\"%s\">github</a> making changes, create issues or submitting changes."
285
- msgstr ""
286
-
287
- #: inc/templates/credits.php:25
288
- msgid "We are Inpsyde"
289
- msgstr ""
290
-
291
- #: inc/templates/credits.php:26
292
- msgid ""
293
- "Inpsyde has developed enterprise solutions with the world’s most popular "
294
- "open-source CMS since it was a kitten. Still do, inconvincible convinced."
295
- msgstr ""
296
-
297
- #: inc/templates/credits.php:28
298
- msgid ""
299
- "Inpsyde is a WordPress <a href=\"%1$s\">VIP Service Partner</a> and <a "
300
- "href=\"%2$s\">WooCommerce Expert</a>."
301
- msgstr ""
302
-
303
- #: inc/templates/credits.php:33
304
- msgid "Look at our other <a href=\"%s\">free WordPress plugins</a>."
305
- msgstr ""
306
-
307
- #: inc/templates/credits.php:38
308
- msgid "Working at Inpsyde"
309
- msgstr ""
310
-
311
- #: inc/templates/credits.php:39
312
- msgid ""
313
- "The biggest WordPress enterprise in Europe we’re dynamically growing and "
314
- "constantly looking for new employees. So do you want to shape WordPress in "
315
- "an interesting and exciting working environment? Here we are!"
316
- msgstr ""
317
-
318
- #: inc/templates/credits.php:40
319
- msgid ""
320
- "At the moment we’re looking for developers for WordPress based products and "
321
- "services. If you’re not a developer and want to be part of us, we’d be "
322
- "happy to recieve your unsolicited application. At Inpsyde you can expect an "
323
- "open, modern and lively company culture:"
324
- msgstr ""
325
-
326
- #: inc/templates/credits.php:42
327
- msgid "challenging and exciting projects"
328
- msgstr ""
329
-
330
- #: inc/templates/credits.php:43
331
- msgid "flexible working hours in remote office"
332
- msgstr ""
333
-
334
- #: inc/templates/credits.php:44
335
- msgid "deliberately flat hierarchies and short decision paths"
336
- msgstr ""
337
-
338
- #: inc/templates/credits.php:45
339
- msgid "a wide variety of tasks"
340
- msgstr ""
341
-
342
- #: inc/templates/credits.php:46
343
- msgid "freedom for personal development and responsible, self-reliant action"
344
- msgstr ""
345
-
346
- #: inc/templates/credits.php:50
347
- msgid ""
348
- "If you love open source and especially WordPress, if you love to organize "
349
- "your working days by yourself and want to use your pragmatic "
350
- "problem-solving skills and result-oriented work methods: <a "
351
- "href=\"%s\">join our team</a>!"
352
- msgstr ""
353
-
354
- #: inc/templates/db_backup.php:12
355
- msgid "Create a backup of your database by clicking \"Create SQL File\"."
356
- msgstr ""
357
-
358
- #: inc/templates/replace_domain.php:11
359
- msgid ""
360
- "If you want to migrate your site to another domain, enter the new URL in "
361
- "the field \"Replace with\" and create a backup of your database by clicking "
362
- "\"Do Replace Domain/Url\"."
363
- msgstr ""
364
-
365
- #: inc/templates/replace_domain.php:20 inc/templates/search_replace.php:15
366
- msgid "Search for: "
367
- msgstr ""
368
-
369
- #: inc/templates/replace_domain.php:24 inc/templates/search_replace.php:19
370
- msgid "Replace with: "
371
- msgstr ""
372
-
373
- #: inc/templates/replace_domain.php:25
374
- msgid "New URL"
375
- msgstr ""
376
-
377
- #: inc/templates/replace_domain.php:28
378
- msgid "Change database prefix"
379
- msgstr ""
380
-
381
- #: inc/templates/replace_domain.php:32
382
- msgid "Current prefix: "
383
- msgstr ""
384
-
385
- #: inc/templates/replace_domain.php:36
386
- msgid "New prefix: "
387
- msgstr ""
388
-
389
- #: inc/templates/replace_domain.php:37
390
- msgid "New database prefix"
391
- msgstr ""
392
-
393
- #: inc/templates/search_replace.php:23
394
- msgid "Select tables"
395
- msgstr ""
396
-
397
- #: inc/templates/search_replace.php:27
398
- msgid "Select all tables"
399
- msgstr ""
400
-
401
- #: inc/templates/search_replace.php:33
402
- msgid "Dry Run"
403
- msgstr ""
404
-
405
- #: inc/templates/search_replace.php:37
406
- msgid "Export SQL file or write changes to DB?"
407
- msgstr ""
408
-
409
- #: inc/templates/search_replace.php:39
410
- msgid "Export SQL file with changes"
411
- msgstr ""
412
-
413
- #: inc/templates/search_replace.php:41
414
- msgid "Save changes to Database"
415
- msgstr ""
416
-
417
- #: inc/templates/search_replace.php:45
418
- msgid "Use GZ compression"
419
- msgstr ""
420
-
421
- #: inc/templates/sql_import.php:16
422
- msgid "Select SQL file to upload. "
423
- msgstr ""
424
-
425
- #: inc/templates/sql_import.php:20
426
- msgid "Maximum file size: "
427
- msgstr ""
428
-
429
- #: inspyde-search-replace.php:52
430
- msgid ""
431
- "This plugin can not be activated because it requires at least PHP version "
432
- "%1$s. "
433
- msgstr ""
434
-
435
- #: inspyde-search-replace.php:55
436
- msgid "back"
437
- msgstr ""
438
-
439
- #. Plugin URI of the plugin/theme
440
- msgid "https://wordpress.org/plugins/search-and-replace/"
441
- msgstr ""
442
-
443
- #. Description of the plugin/theme
444
- msgid ""
445
- "Search & Replace data in your whole WordPress setup, backup and import your "
446
- "database, change table prefix or migrate your domain to another domain."
447
- msgstr ""
448
-
449
- #. Author of the plugin/theme
450
- msgid "Inpsyde GmbH"
451
- msgstr ""
452
-
453
- #. Author URI of the plugin/theme
454
- msgid "http://inpsyde.com"
455
- msgstr ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -1,22 +1,36 @@
1
  === Search & Replace ===
2
- Contributors: inpsyde, Bueltge, derpixler, ChriCo, s-hinse, Giede
3
  Tags: search, replace, backup, import, sql, migrate, multisite
4
  Requires at least: 4.0
5
- Tested up to: 4.9
6
- Stable tag: 3.1.1
 
 
 
7
 
8
  Search & Replace data in your database with WordPress admin, replace domains/URLs of your WordPress installation.
9
 
10
  == Description ==
11
  With Search & Replace you can search for everything and replace this with everything **but before** you do this you can easily **create** a simple **database backup** and restore it.
12
 
13
- **We have implemented special features!**
 
 
 
 
 
 
 
 
 
 
 
14
  The first one is "Replace a Domain / Url" that is useful for a quick and simple transfer or a migration of an WordPress.
15
  The second is a full support for serialized data but there are a lot more features - find them!
16
 
17
  Our goal with this plugin is to give you a good solution for both Developers and Users of WordPress.
18
 
19
- > **Note:** This plugin requires PHP 5.4 or higher to be activated.
20
 
21
  [**Checkout our GitHub Repository**](https://github.com/inpsyde/search-and-replace)
22
 
@@ -39,7 +53,7 @@ You want to donate - we prefer a [positive review](https://wordpress.org/support
39
  == Installation ==
40
  = Requirements =
41
  - WordPress 4.0 (Single and Multisite)
42
- - PHP 5.4, newer PHP versions will work faster.
43
 
44
  == Screenshots ==
45
  1. Search and Replace
@@ -49,8 +63,14 @@ You want to donate - we prefer a [positive review](https://wordpress.org/support
49
  5. Result screen after search or search and replace
50
 
51
  == Changelog ==
52
-
53
- = v3.1.1 (2016-12-31) =
 
 
 
 
 
 
54
  * hotfix: prevent declaration error with Requisite
55
 
56
  = v3.1.1 (2016-12-24) =
1
  === Search & Replace ===
2
+ Contributors: inpsyde, Bueltge, derpixler, ChriCo, s-hinse
3
  Tags: search, replace, backup, import, sql, migrate, multisite
4
  Requires at least: 4.0
5
+ Tested up to: 5.1
6
+ Requires PHP: 5.6
7
+ Stable tag: 3.2.0
8
+ License: GPLv3 or later
9
+ License URI: https://www.gnu.org/licenses/gpl-3.0.html
10
 
11
  Search & Replace data in your database with WordPress admin, replace domains/URLs of your WordPress installation.
12
 
13
  == Description ==
14
  With Search & Replace you can search for everything and replace this with everything **but before** you do this you can easily **create** a simple **database backup** and restore it.
15
 
16
+ For avoiding problem please keep in mind that this plugin search and replace strictly what is specified in "Replace With".
17
+ Common error example:
18
+ Search For: http://web.com/wordpress/
19
+ Replace With: http://neuweb.com/wordpress
20
+ The url become something like: http://neuweb.com/wordpresscontent/...
21
+
22
+ Correct "Replace with":
23
+ Search For: http://web.com/wordpress/
24
+ Replace With: http://neuweb.com/wordpress/
25
+ The url become something like: http://neuweb.com/wordpress/content/...
26
+
27
+ **We have implements special features!**
28
  The first one is "Replace a Domain / Url" that is useful for a quick and simple transfer or a migration of an WordPress.
29
  The second is a full support for serialized data but there are a lot more features - find them!
30
 
31
  Our goal with this plugin is to give you a good solution for both Developers and Users of WordPress.
32
 
33
+ > **Note:** This plugin requires PHP 5.6 or higher to be activated.
34
 
35
  [**Checkout our GitHub Repository**](https://github.com/inpsyde/search-and-replace)
36
 
53
  == Installation ==
54
  = Requirements =
55
  - WordPress 4.0 (Single and Multisite)
56
+ - PHP 5.6, newer PHP versions will work faster.
57
 
58
  == Screenshots ==
59
  1. Search and Replace
63
  5. Result screen after search or search and replace
64
 
65
  == Changelog ==
66
+ = 3.2.0 (2019-01-17) =
67
+ * Added CSV format alternative for search/replace [#82](https://github.com/inpsyde/search-and-replace/issues/82).
68
+ * Improve code structure, preparation for more solid UnitTests.
69
+ * Improve Modal Table UI.
70
+ * Added Multiline searching [#119](https://github.com/inpsyde/search-and-replace/issues/119).
71
+ * Fix several issues to run always with php 5.6 to 7.2.
72
+
73
+ = v3.1.2 (2016-12-31) =
74
  * hotfix: prevent declaration error with Requisite
75
 
76
  = v3.1.1 (2016-12-24) =
vendor/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInit89cbb49124c536405f852edfc948afee::getLoader();
vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see http://www.php-fig.org/psr/psr-0/
41
+ * @see http://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ // PSR-4
46
+ private $prefixLengthsPsr4 = array();
47
+ private $prefixDirsPsr4 = array();
48
+ private $fallbackDirsPsr4 = array();
49
+
50
+ // PSR-0
51
+ private $prefixesPsr0 = array();
52
+ private $fallbackDirsPsr0 = array();
53
+
54
+ private $useIncludePath = false;
55
+ private $classMap = array();
56
+ private $classMapAuthoritative = false;
57
+ private $missingClasses = array();
58
+ private $apcuPrefix;
59
+
60
+ public function getPrefixes()
61
+ {
62
+ if (!empty($this->prefixesPsr0)) {
63
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
64
+ }
65
+
66
+ return array();
67
+ }
68
+
69
+ public function getPrefixesPsr4()
70
+ {
71
+ return $this->prefixDirsPsr4;
72
+ }
73
+
74
+ public function getFallbackDirs()
75
+ {
76
+ return $this->fallbackDirsPsr0;
77
+ }
78
+
79
+ public function getFallbackDirsPsr4()
80
+ {
81
+ return $this->fallbackDirsPsr4;
82
+ }
83
+
84
+ public function getClassMap()
85
+ {
86
+ return $this->classMap;
87
+ }
88
+
89
+ /**
90
+ * @param array $classMap Class to filename map
91
+ */
92
+ public function addClassMap(array $classMap)
93
+ {
94
+ if ($this->classMap) {
95
+ $this->classMap = array_merge($this->classMap, $classMap);
96
+ } else {
97
+ $this->classMap = $classMap;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Registers a set of PSR-0 directories for a given prefix, either
103
+ * appending or prepending to the ones previously set for this prefix.
104
+ *
105
+ * @param string $prefix The prefix
106
+ * @param array|string $paths The PSR-0 root directories
107
+ * @param bool $prepend Whether to prepend the directories
108
+ */
109
+ public function add($prefix, $paths, $prepend = false)
110
+ {
111
+ if (!$prefix) {
112
+ if ($prepend) {
113
+ $this->fallbackDirsPsr0 = array_merge(
114
+ (array) $paths,
115
+ $this->fallbackDirsPsr0
116
+ );
117
+ } else {
118
+ $this->fallbackDirsPsr0 = array_merge(
119
+ $this->fallbackDirsPsr0,
120
+ (array) $paths
121
+ );
122
+ }
123
+
124
+ return;
125
+ }
126
+
127
+ $first = $prefix[0];
128
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
129
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
130
+
131
+ return;
132
+ }
133
+ if ($prepend) {
134
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
135
+ (array) $paths,
136
+ $this->prefixesPsr0[$first][$prefix]
137
+ );
138
+ } else {
139
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
140
+ $this->prefixesPsr0[$first][$prefix],
141
+ (array) $paths
142
+ );
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Registers a set of PSR-4 directories for a given namespace, either
148
+ * appending or prepending to the ones previously set for this namespace.
149
+ *
150
+ * @param string $prefix The prefix/namespace, with trailing '\\'
151
+ * @param array|string $paths The PSR-4 base directories
152
+ * @param bool $prepend Whether to prepend the directories
153
+ *
154
+ * @throws \InvalidArgumentException
155
+ */
156
+ public function addPsr4($prefix, $paths, $prepend = false)
157
+ {
158
+ if (!$prefix) {
159
+ // Register directories for the root namespace.
160
+ if ($prepend) {
161
+ $this->fallbackDirsPsr4 = array_merge(
162
+ (array) $paths,
163
+ $this->fallbackDirsPsr4
164
+ );
165
+ } else {
166
+ $this->fallbackDirsPsr4 = array_merge(
167
+ $this->fallbackDirsPsr4,
168
+ (array) $paths
169
+ );
170
+ }
171
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
172
+ // Register directories for a new namespace.
173
+ $length = strlen($prefix);
174
+ if ('\\' !== $prefix[$length - 1]) {
175
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
176
+ }
177
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
178
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
179
+ } elseif ($prepend) {
180
+ // Prepend directories for an already registered namespace.
181
+ $this->prefixDirsPsr4[$prefix] = array_merge(
182
+ (array) $paths,
183
+ $this->prefixDirsPsr4[$prefix]
184
+ );
185
+ } else {
186
+ // Append directories for an already registered namespace.
187
+ $this->prefixDirsPsr4[$prefix] = array_merge(
188
+ $this->prefixDirsPsr4[$prefix],
189
+ (array) $paths
190
+ );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Registers a set of PSR-0 directories for a given prefix,
196
+ * replacing any others previously set for this prefix.
197
+ *
198
+ * @param string $prefix The prefix
199
+ * @param array|string $paths The PSR-0 base directories
200
+ */
201
+ public function set($prefix, $paths)
202
+ {
203
+ if (!$prefix) {
204
+ $this->fallbackDirsPsr0 = (array) $paths;
205
+ } else {
206
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Registers a set of PSR-4 directories for a given namespace,
212
+ * replacing any others previously set for this namespace.
213
+ *
214
+ * @param string $prefix The prefix/namespace, with trailing '\\'
215
+ * @param array|string $paths The PSR-4 base directories
216
+ *
217
+ * @throws \InvalidArgumentException
218
+ */
219
+ public function setPsr4($prefix, $paths)
220
+ {
221
+ if (!$prefix) {
222
+ $this->fallbackDirsPsr4 = (array) $paths;
223
+ } else {
224
+ $length = strlen($prefix);
225
+ if ('\\' !== $prefix[$length - 1]) {
226
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
227
+ }
228
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
229
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Turns on searching the include path for class files.
235
+ *
236
+ * @param bool $useIncludePath
237
+ */
238
+ public function setUseIncludePath($useIncludePath)
239
+ {
240
+ $this->useIncludePath = $useIncludePath;
241
+ }
242
+
243
+ /**
244
+ * Can be used to check if the autoloader uses the include path to check
245
+ * for classes.
246
+ *
247
+ * @return bool
248
+ */
249
+ public function getUseIncludePath()
250
+ {
251
+ return $this->useIncludePath;
252
+ }
253
+
254
+ /**
255
+ * Turns off searching the prefix and fallback directories for classes
256
+ * that have not been registered with the class map.
257
+ *
258
+ * @param bool $classMapAuthoritative
259
+ */
260
+ public function setClassMapAuthoritative($classMapAuthoritative)
261
+ {
262
+ $this->classMapAuthoritative = $classMapAuthoritative;
263
+ }
264
+
265
+ /**
266
+ * Should class lookup fail if not found in the current class map?
267
+ *
268
+ * @return bool
269
+ */
270
+ public function isClassMapAuthoritative()
271
+ {
272
+ return $this->classMapAuthoritative;
273
+ }
274
+
275
+ /**
276
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277
+ *
278
+ * @param string|null $apcuPrefix
279
+ */
280
+ public function setApcuPrefix($apcuPrefix)
281
+ {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
283
+ }
284
+
285
+ /**
286
+ * The APCu prefix in use, or null if APCu caching is not enabled.
287
+ *
288
+ * @return string|null
289
+ */
290
+ public function getApcuPrefix()
291
+ {
292
+ return $this->apcuPrefix;
293
+ }
294
+
295
+ /**
296
+ * Registers this instance as an autoloader.
297
+ *
298
+ * @param bool $prepend Whether to prepend the autoloader or not
299
+ */
300
+ public function register($prepend = false)
301
+ {
302
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
303
+ }
304
+
305
+ /**
306
+ * Unregisters this instance as an autoloader.
307
+ */
308
+ public function unregister()
309
+ {
310
+ spl_autoload_unregister(array($this, 'loadClass'));
311
+ }
312
+
313
+ /**
314
+ * Loads the given class or interface.
315
+ *
316
+ * @param string $class The name of the class
317
+ * @return bool|null True if loaded, null otherwise
318
+ */
319
+ public function loadClass($class)
320
+ {
321
+ if ($file = $this->findFile($class)) {
322
+ includeFile($file);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Finds the path to the file where the class is defined.
330
+ *
331
+ * @param string $class The name of the class
332
+ *
333
+ * @return string|false The path if found, false otherwise
334
+ */
335
+ public function findFile($class)
336
+ {
337
+ // class map lookup
338
+ if (isset($this->classMap[$class])) {
339
+ return $this->classMap[$class];
340
+ }
341
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
342
+ return false;
343
+ }
344
+ if (null !== $this->apcuPrefix) {
345
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
346
+ if ($hit) {
347
+ return $file;
348
+ }
349
+ }
350
+
351
+ $file = $this->findFileWithExtension($class, '.php');
352
+
353
+ // Search for Hack files if we are running on HHVM
354
+ if (false === $file && defined('HHVM_VERSION')) {
355
+ $file = $this->findFileWithExtension($class, '.hh');
356
+ }
357
+
358
+ if (null !== $this->apcuPrefix) {
359
+ apcu_add($this->apcuPrefix.$class, $file);
360
+ }
361
+
362
+ if (false === $file) {
363
+ // Remember that this class does not exist.
364
+ $this->missingClasses[$class] = true;
365
+ }
366
+
367
+ return $file;
368
+ }
369
+
370
+ private function findFileWithExtension($class, $ext)
371
+ {
372
+ // PSR-4 lookup
373
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
374
+
375
+ $first = $class[0];
376
+ if (isset($this->prefixLengthsPsr4[$first])) {
377
+ $subPath = $class;
378
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
379
+ $subPath = substr($subPath, 0, $lastPos);
380
+ $search = $subPath . '\\';
381
+ if (isset($this->prefixDirsPsr4[$search])) {
382
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
383
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
384
+ if (file_exists($file = $dir . $pathEnd)) {
385
+ return $file;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ // PSR-4 fallback dirs
393
+ foreach ($this->fallbackDirsPsr4 as $dir) {
394
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
395
+ return $file;
396
+ }
397
+ }
398
+
399
+ // PSR-0 lookup
400
+ if (false !== $pos = strrpos($class, '\\')) {
401
+ // namespaced class name
402
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
403
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
404
+ } else {
405
+ // PEAR-like class name
406
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
407
+ }
408
+
409
+ if (isset($this->prefixesPsr0[$first])) {
410
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
411
+ if (0 === strpos($class, $prefix)) {
412
+ foreach ($dirs as $dir) {
413
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
414
+ return $file;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ // PSR-0 fallback dirs
422
+ foreach ($this->fallbackDirsPsr0 as $dir) {
423
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
424
+ return $file;
425
+ }
426
+ }
427
+
428
+ // PSR-0 include paths.
429
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
430
+ return $file;
431
+ }
432
+
433
+ return false;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Scope isolated include.
439
+ *
440
+ * Prevents access to $this/self from included files.
441
+ */
442
+ function includeFile($file)
443
+ {
444
+ include $file;
445
+ }
vendor/composer/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Copyright (c) Nils Adermann, Jordi Boggiano
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'Inpsyde\\SearchReplace\\Database\\Exporter' => $baseDir . '/inc/Database/Exporter.php',
10
+ 'Inpsyde\\SearchReplace\\Database\\Importer' => $baseDir . '/inc/Database/Importer.php',
11
+ 'Inpsyde\\SearchReplace\\Database\\Manager' => $baseDir . '/inc/Database/Manager.php',
12
+ 'Inpsyde\\SearchReplace\\Database\\Replace' => $baseDir . '/inc/Database/Replace.php',
13
+ 'Inpsyde\\SearchReplace\\FileDownloader' => $baseDir . '/inc/FileDownloader.php',
14
+ 'Inpsyde\\SearchReplace\\Page\\AbstractPage' => $baseDir . '/inc/Page/AbstractPage.php',
15
+ 'Inpsyde\\SearchReplace\\Page\\BackupDatabase' => $baseDir . '/inc/Page/BackupDatabase.php',
16
+ 'Inpsyde\\SearchReplace\\Page\\Credits' => $baseDir . '/inc/Page/Credits.php',
17
+ 'Inpsyde\\SearchReplace\\Page\\Manager' => $baseDir . '/inc/Page/Manager.php',
18
+ 'Inpsyde\\SearchReplace\\Page\\PageInterface' => $baseDir . '/inc/Page/PageInterface.php',
19
+ 'Inpsyde\\SearchReplace\\Page\\ReplaceDomain' => $baseDir . '/inc/Page/ReplaceDomain.php',
20
+ 'Inpsyde\\SearchReplace\\Page\\SearchReplace' => $baseDir . '/inc/Page/SearchReplace.php',
21
+ 'Inpsyde\\SearchReplace\\Page\\SqlImport' => $baseDir . '/inc/Page/SqlImport.php',
22
+ 'Inpsyde\\SearchReplace\\Service\\MaxExecutionTime' => $baseDir . '/inc/Service/MaxExecutionTime.php',
23
+ );
vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'Inpsyde\\SearchReplace\\' => array($baseDir . '/inc'),
10
+ );
vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInit89cbb49124c536405f852edfc948afee
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ public static function getLoader()
17
+ {
18
+ if (null !== self::$loader) {
19
+ return self::$loader;
20
+ }
21
+
22
+ spl_autoload_register(array('ComposerAutoloaderInit89cbb49124c536405f852edfc948afee', 'loadClassLoader'), true, true);
23
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit89cbb49124c536405f852edfc948afee', 'loadClassLoader'));
25
+
26
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
+ if ($useStaticLoader) {
28
+ require_once __DIR__ . '/autoload_static.php';
29
+
30
+ call_user_func(\Composer\Autoload\ComposerStaticInit89cbb49124c536405f852edfc948afee::getInitializer($loader));
31
+ } else {
32
+ $map = require __DIR__ . '/autoload_namespaces.php';
33
+ foreach ($map as $namespace => $path) {
34
+ $loader->set($namespace, $path);
35
+ }
36
+
37
+ $map = require __DIR__ . '/autoload_psr4.php';
38
+ foreach ($map as $namespace => $path) {
39
+ $loader->setPsr4($namespace, $path);
40
+ }
41
+
42
+ $classMap = require __DIR__ . '/autoload_classmap.php';
43
+ if ($classMap) {
44
+ $loader->addClassMap($classMap);
45
+ }
46
+ }
47
+
48
+ $loader->register(true);
49
+
50
+ return $loader;
51
+ }
52
+ }
vendor/composer/autoload_static.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInit89cbb49124c536405f852edfc948afee
8
+ {
9
+ public static $prefixLengthsPsr4 = array (
10
+ 'I' =>
11
+ array (
12
+ 'Inpsyde\\SearchReplace\\' => 22,
13
+ ),
14
+ );
15
+
16
+ public static $prefixDirsPsr4 = array (
17
+ 'Inpsyde\\SearchReplace\\' =>
18
+ array (
19
+ 0 => __DIR__ . '/../..' . '/inc',
20
+ ),
21
+ );
22
+
23
+ public static $classMap = array (
24
+ 'Inpsyde\\SearchReplace\\Database\\Exporter' => __DIR__ . '/../..' . '/inc/Database/Exporter.php',
25
+ 'Inpsyde\\SearchReplace\\Database\\Importer' => __DIR__ . '/../..' . '/inc/Database/Importer.php',
26
+ 'Inpsyde\\SearchReplace\\Database\\Manager' => __DIR__ . '/../..' . '/inc/Database/Manager.php',
27
+ 'Inpsyde\\SearchReplace\\Database\\Replace' => __DIR__ . '/../..' . '/inc/Database/Replace.php',
28
+ 'Inpsyde\\SearchReplace\\FileDownloader' => __DIR__ . '/../..' . '/inc/FileDownloader.php',
29
+ 'Inpsyde\\SearchReplace\\Page\\AbstractPage' => __DIR__ . '/../..' . '/inc/Page/AbstractPage.php',
30
+ 'Inpsyde\\SearchReplace\\Page\\BackupDatabase' => __DIR__ . '/../..' . '/inc/Page/BackupDatabase.php',
31
+ 'Inpsyde\\SearchReplace\\Page\\Credits' => __DIR__ . '/../..' . '/inc/Page/Credits.php',
32
+ 'Inpsyde\\SearchReplace\\Page\\Manager' => __DIR__ . '/../..' . '/inc/Page/Manager.php',
33
+ 'Inpsyde\\SearchReplace\\Page\\PageInterface' => __DIR__ . '/../..' . '/inc/Page/PageInterface.php',
34
+ 'Inpsyde\\SearchReplace\\Page\\ReplaceDomain' => __DIR__ . '/../..' . '/inc/Page/ReplaceDomain.php',
35
+ 'Inpsyde\\SearchReplace\\Page\\SearchReplace' => __DIR__ . '/../..' . '/inc/Page/SearchReplace.php',
36
+ 'Inpsyde\\SearchReplace\\Page\\SqlImport' => __DIR__ . '/../..' . '/inc/Page/SqlImport.php',
37
+ 'Inpsyde\\SearchReplace\\Service\\MaxExecutionTime' => __DIR__ . '/../..' . '/inc/Service/MaxExecutionTime.php',
38
+ );
39
+
40
+ public static function getInitializer(ClassLoader $loader)
41
+ {
42
+ return \Closure::bind(function () use ($loader) {
43
+ $loader->prefixLengthsPsr4 = ComposerStaticInit89cbb49124c536405f852edfc948afee::$prefixLengthsPsr4;
44
+ $loader->prefixDirsPsr4 = ComposerStaticInit89cbb49124c536405f852edfc948afee::$prefixDirsPsr4;
45
+ $loader->classMap = ComposerStaticInit89cbb49124c536405f852edfc948afee::$classMap;
46
+
47
+ }, null, ClassLoader::class);
48
+ }
49
+ }
vendor/composer/installed.json ADDED
@@ -0,0 +1,2601 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "name": "antecedent/patchwork",
4
+ "version": "2.1.8",
5
+ "version_normalized": "2.1.8.0",
6
+ "source": {
7
+ "type": "git",
8
+ "url": "https://github.com/antecedent/patchwork.git",
9
+ "reference": "3bb81ace3914c220aa273d1c0603d5e1b454c0d7"
10
+ },
11
+ "dist": {
12
+ "type": "zip",
13
+ "url": "https://api.github.com/repos/antecedent/patchwork/zipball/3bb81ace3914c220aa273d1c0603d5e1b454c0d7",
14
+ "reference": "3bb81ace3914c220aa273d1c0603d5e1b454c0d7",
15
+ "shasum": ""
16
+ },
17
+ "require": {
18
+ "php": ">=5.4.0"
19
+ },
20
+ "time": "2018-02-19T18:52:50+00:00",
21
+ "type": "library",
22
+ "installation-source": "dist",
23
+ "notification-url": "https://packagist.org/downloads/",
24
+ "license": [
25
+ "MIT"
26
+ ],
27
+ "authors": [
28
+ {
29
+ "name": "Ignas Rudaitis",
30
+ "email": "ignas.rudaitis@gmail.com"
31
+ }
32
+ ],
33
+ "description": "Method redefinition (monkey-patching) functionality for PHP.",
34
+ "homepage": "http://patchwork2.org/",
35
+ "keywords": [
36
+ "aop",
37
+ "aspect",
38
+ "interception",
39
+ "monkeypatching",
40
+ "redefinition",
41
+ "runkit",
42
+ "testing"
43
+ ]
44
+ },
45
+ {
46
+ "name": "brain/monkey",
47
+ "version": "2.2.0",
48
+ "version_normalized": "2.2.0.0",
49
+ "source": {
50
+ "type": "git",
51
+ "url": "https://github.com/Brain-WP/BrainMonkey.git",
52
+ "reference": "ed9e0698bc1292f33698719da8ca1aa2e18acc51"
53
+ },
54
+ "dist": {
55
+ "type": "zip",
56
+ "url": "https://api.github.com/repos/Brain-WP/BrainMonkey/zipball/ed9e0698bc1292f33698719da8ca1aa2e18acc51",
57
+ "reference": "ed9e0698bc1292f33698719da8ca1aa2e18acc51",
58
+ "shasum": ""
59
+ },
60
+ "require": {
61
+ "antecedent/patchwork": "^2.0",
62
+ "mockery/mockery": ">=0.9 <2",
63
+ "php": ">=5.6.0"
64
+ },
65
+ "require-dev": {
66
+ "phpunit/phpunit": "~5.7.9"
67
+ },
68
+ "time": "2017-12-01T16:32:09+00:00",
69
+ "type": "library",
70
+ "extra": {
71
+ "branch-alias": {
72
+ "dev-version/1": "1.x-dev",
73
+ "dev-master": "2.0.x-dev"
74
+ }
75
+ },
76
+ "installation-source": "dist",
77
+ "autoload": {
78
+ "psr-4": {
79
+ "Brain\\Monkey\\": "src/"
80
+ },
81
+ "files": [
82
+ "inc/api.php"
83
+ ]
84
+ },
85
+ "notification-url": "https://packagist.org/downloads/",
86
+ "license": [
87
+ "MIT"
88
+ ],
89
+ "authors": [
90
+ {
91
+ "name": "Giuseppe Mazzapica",
92
+ "email": "giuseppe.mazzapica@gmail.com",
93
+ "homepage": "https://gmazzap.me",
94
+ "role": "Developer"
95
+ }
96
+ ],
97
+ "description": "Mocking utility for PHP functions and WordPress plugin API",
98
+ "keywords": [
99
+ "Monkey Patching",
100
+ "interception",
101
+ "mock",
102
+ "mock functions",
103
+ "mockery",
104
+ "patchwork",
105
+ "redefinition",
106
+ "runkit",
107
+ "test",
108
+ "testing"
109
+ ]
110
+ },
111
+ {
112
+ "name": "composer/semver",
113
+ "version": "1.4.2",
114
+ "version_normalized": "1.4.2.0",
115
+ "source": {
116
+ "type": "git",
117
+ "url": "https://github.com/composer/semver.git",
118
+ "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573"
119
+ },
120
+ "dist": {
121
+ "type": "zip",
122
+ "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573",
123
+ "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573",
124
+ "shasum": ""
125
+ },
126
+ "require": {
127
+ "php": "^5.3.2 || ^7.0"
128
+ },
129
+ "require-dev": {
130
+ "phpunit/phpunit": "^4.5 || ^5.0.5",
131
+ "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
132
+ },
133
+ "time": "2016-08-30T16:08:34+00:00",
134
+ "type": "library",
135
+ "extra": {
136
+ "branch-alias": {
137
+ "dev-master": "1.x-dev"
138
+ }
139
+ },
140
+ "installation-source": "dist",
141
+ "autoload": {
142
+ "psr-4": {
143
+ "Composer\\Semver\\": "src"
144
+ }
145
+ },
146
+ "notification-url": "https://packagist.org/downloads/",
147
+ "license": [
148
+ "MIT"
149
+ ],
150
+ "authors": [
151
+ {
152
+ "name": "Nils Adermann",
153
+ "email": "naderman@naderman.de",
154
+ "homepage": "http://www.naderman.de"
155
+ },
156
+ {
157
+ "name": "Jordi Boggiano",
158
+ "email": "j.boggiano@seld.be",
159
+ "homepage": "http://seld.be"
160
+ },
161
+ {
162
+ "name": "Rob Bast",
163
+ "email": "rob.bast@gmail.com",
164
+ "homepage": "http://robbast.nl"
165
+ }
166
+ ],
167
+ "description": "Semver library that offers utilities, version constraint parsing and validation.",
168
+ "keywords": [
169
+ "semantic",
170
+ "semver",
171
+ "validation",
172
+ "versioning"
173
+ ]
174
+ },
175
+ {
176
+ "name": "composer/xdebug-handler",
177
+ "version": "1.3.1",
178
+ "version_normalized": "1.3.1.0",
179
+ "source": {
180
+ "type": "git",
181
+ "url": "https://github.com/composer/xdebug-handler.git",
182
+ "reference": "dc523135366eb68f22268d069ea7749486458562"
183
+ },
184
+ "dist": {
185
+ "type": "zip",
186
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/dc523135366eb68f22268d069ea7749486458562",
187
+ "reference": "dc523135366eb68f22268d069ea7749486458562",
188
+ "shasum": ""
189
+ },
190
+ "require": {
191
+ "php": "^5.3.2 || ^7.0",
192
+ "psr/log": "^1.0"
193
+ },
194
+ "require-dev": {
195
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5"
196
+ },
197
+ "time": "2018-11-29T10:59:02+00:00",
198
+ "type": "library",
199
+ "installation-source": "dist",
200
+ "autoload": {
201
+ "psr-4": {
202
+ "Composer\\XdebugHandler\\": "src"
203
+ }
204
+ },
205
+ "notification-url": "https://packagist.org/downloads/",
206
+ "license": [
207
+ "MIT"
208
+ ],
209
+ "authors": [
210
+ {
211
+ "name": "John Stevenson",
212
+ "email": "john-stevenson@blueyonder.co.uk"
213
+ }
214
+ ],
215
+ "description": "Restarts a process without xdebug.",
216
+ "keywords": [
217
+ "Xdebug",
218
+ "performance"
219
+ ]
220
+ },
221
+ {
222
+ "name": "dg/composer-cleaner",
223
+ "version": "v2.0",
224
+ "version_normalized": "2.0.0.0",
225
+ "source": {
226
+ "type": "git",
227
+ "url": "https://github.com/dg/composer-cleaner.git",
228
+ "reference": "41db7585dc9f2f345020931c76091dd3936df930"
229
+ },
230
+ "dist": {
231
+ "type": "zip",
232
+ "url": "https://api.github.com/repos/dg/composer-cleaner/zipball/41db7585dc9f2f345020931c76091dd3936df930",
233
+ "reference": "41db7585dc9f2f345020931c76091dd3936df930",
234
+ "shasum": ""
235
+ },
236
+ "require": {
237
+ "composer-plugin-api": "^1.0",
238
+ "php": ">=5.4.0"
239
+ },
240
+ "require-dev": {
241
+ "nette/tester": "^1.7"
242
+ },
243
+ "time": "2017-09-20T12:41:46+00:00",
244
+ "type": "composer-plugin",
245
+ "extra": {
246
+ "class": "DG\\ComposerCleaner\\Plugin"
247
+ },
248
+ "installation-source": "dist",
249
+ "autoload": {
250
+ "classmap": [
251
+ "src/"
252
+ ]
253
+ },
254
+ "notification-url": "https://packagist.org/downloads/",
255
+ "license": [
256
+ "BSD-3-Clause"
257
+ ],
258
+ "authors": [
259
+ {
260
+ "name": "David Grudl",
261
+ "homepage": "https://davidgrudl.com"
262
+ }
263
+ ],
264
+ "description": "Victor The Cleaner: removes unnecessary files from vendor directory.",
265
+ "keywords": [
266
+ "composer"
267
+ ]
268
+ },
269
+ {
270
+ "name": "doctrine/instantiator",
271
+ "version": "1.1.0",
272
+ "version_normalized": "1.1.0.0",
273
+ "source": {
274
+ "type": "git",
275
+ "url": "https://github.com/doctrine/instantiator.git",
276
+ "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda"
277
+ },
278
+ "dist": {
279
+ "type": "zip",
280
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda",
281
+ "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda",
282
+ "shasum": ""
283
+ },
284
+ "require": {
285
+ "php": "^7.1"
286
+ },
287
+ "require-dev": {
288
+ "athletic/athletic": "~0.1.8",
289
+ "ext-pdo": "*",
290
+ "ext-phar": "*",
291
+ "phpunit/phpunit": "^6.2.3",
292
+ "squizlabs/php_codesniffer": "^3.0.2"
293
+ },
294
+ "time": "2017-07-22T11:58:36+00:00",
295
+ "type": "library",
296
+ "extra": {
297
+ "branch-alias": {
298
+ "dev-master": "1.2.x-dev"
299
+ }
300
+ },
301
+ "installation-source": "dist",
302
+ "autoload": {
303
+ "psr-4": {
304
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
305
+ }
306
+ },
307
+ "notification-url": "https://packagist.org/downloads/",
308
+ "license": [
309
+ "MIT"
310
+ ],
311
+ "authors": [
312
+ {
313
+ "name": "Marco Pivetta",
314
+ "email": "ocramius@gmail.com",
315
+ "homepage": "http://ocramius.github.com/"
316
+ }
317
+ ],
318
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
319
+ "homepage": "https://github.com/doctrine/instantiator",
320
+ "keywords": [
321
+ "constructor",
322
+ "instantiate"
323
+ ]
324
+ },
325
+ {
326
+ "name": "felixfbecker/advanced-json-rpc",
327
+ "version": "v3.0.3",
328
+ "version_normalized": "3.0.3.0",
329
+ "source": {
330
+ "type": "git",
331
+ "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git",
332
+ "reference": "241c470695366e7b83672be04ea0e64d8085a551"
333
+ },
334
+ "dist": {
335
+ "type": "zip",
336
+ "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/241c470695366e7b83672be04ea0e64d8085a551",
337
+ "reference": "241c470695366e7b83672be04ea0e64d8085a551",
338
+ "shasum": ""
339
+ },
340
+ "require": {
341
+ "netresearch/jsonmapper": "^1.0",
342
+ "php": ">=7.0",
343
+ "phpdocumentor/reflection-docblock": "^4.0.0"
344
+ },
345
+ "require-dev": {
346
+ "phpunit/phpunit": "^6.0.0"
347
+ },
348
+ "time": "2018-09-10T08:58:41+00:00",
349
+ "type": "library",
350
+ "installation-source": "dist",
351
+ "autoload": {
352
+ "psr-4": {
353
+ "AdvancedJsonRpc\\": "lib/"
354
+ }
355
+ },
356
+ "notification-url": "https://packagist.org/downloads/",
357
+ "license": [
358
+ "ISC"
359
+ ],
360
+ "authors": [
361
+ {
362
+ "name": "Felix Becker",
363
+ "email": "felix.b@outlook.com"
364
+ }
365
+ ],
366
+ "description": "A more advanced JSONRPC implementation"
367
+ },
368
+ {
369
+ "name": "hamcrest/hamcrest-php",
370
+ "version": "v2.0.0",
371
+ "version_normalized": "2.0.0.0",
372
+ "source": {
373
+ "type": "git",
374
+ "url": "https://github.com/hamcrest/hamcrest-php.git",
375
+ "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad"
376
+ },
377
+ "dist": {
378
+ "type": "zip",
379
+ "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad",
380
+ "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad",
381
+ "shasum": ""
382
+ },
383
+ "require": {
384
+ "php": "^5.3|^7.0"
385
+ },
386
+ "replace": {
387
+ "cordoval/hamcrest-php": "*",
388
+ "davedevelopment/hamcrest-php": "*",
389
+ "kodova/hamcrest-php": "*"
390
+ },
391
+ "require-dev": {
392
+ "phpunit/php-file-iterator": "1.3.3",
393
+ "phpunit/phpunit": "~4.0",
394
+ "satooshi/php-coveralls": "^1.0"
395
+ },
396
+ "time": "2016-01-20T08:20:44+00:00",
397
+ "type": "library",
398
+ "extra": {
399
+ "branch-alias": {
400
+ "dev-master": "2.0-dev"
401
+ }
402
+ },
403
+ "installation-source": "dist",
404
+ "autoload": {
405
+ "classmap": [
406
+ "hamcrest"
407
+ ]
408
+ },
409
+ "notification-url": "https://packagist.org/downloads/",
410
+ "license": [
411
+ "BSD"
412
+ ],
413
+ "description": "This is the PHP port of Hamcrest Matchers",
414
+ "keywords": [
415
+ "test"
416
+ ]
417
+ },
418
+ {
419
+ "name": "microsoft/tolerant-php-parser",
420
+ "version": "v0.0.15",
421
+ "version_normalized": "0.0.15.0",
422
+ "source": {
423
+ "type": "git",
424
+ "url": "https://github.com/Microsoft/tolerant-php-parser.git",
425
+ "reference": "54a84f1250dcde5641e86b5e966fec5f0e201f71"
426
+ },
427
+ "dist": {
428
+ "type": "zip",
429
+ "url": "https://api.github.com/repos/Microsoft/tolerant-php-parser/zipball/54a84f1250dcde5641e86b5e966fec5f0e201f71",
430
+ "reference": "54a84f1250dcde5641e86b5e966fec5f0e201f71",
431
+ "shasum": ""
432
+ },
433
+ "require": {
434
+ "php": ">=7.0"
435
+ },
436
+ "require-dev": {
437
+ "phpunit/phpunit": "^6.4"
438
+ },
439
+ "time": "2018-09-26T05:43:26+00:00",
440
+ "type": "library",
441
+ "installation-source": "dist",
442
+ "autoload": {
443
+ "psr-4": {
444
+ "Microsoft\\PhpParser\\": [
445
+ "src/"
446
+ ]
447
+ }
448
+ },
449
+ "notification-url": "https://packagist.org/downloads/",
450
+ "license": [
451
+ "MIT"
452
+ ],
453
+ "authors": [
454
+ {
455
+ "name": "Rob Lourens",
456
+ "email": "roblou@microsoft.com"
457
+ }
458
+ ],
459
+ "description": "Tolerant PHP-to-AST parser designed for IDE usage scenarios"
460
+ },
461
+ {
462
+ "name": "mockery/mockery",
463
+ "version": "1.2.0",
464
+ "version_normalized": "1.2.0.0",
465
+ "source": {
466
+ "type": "git",
467
+ "url": "https://github.com/mockery/mockery.git",
468
+ "reference": "100633629bf76d57430b86b7098cd6beb996a35a"
469
+ },
470
+ "dist": {
471
+ "type": "zip",
472
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/100633629bf76d57430b86b7098cd6beb996a35a",
473
+ "reference": "100633629bf76d57430b86b7098cd6beb996a35a",
474
+ "shasum": ""
475
+ },
476
+ "require": {
477
+ "hamcrest/hamcrest-php": "~2.0",
478
+ "lib-pcre": ">=7.0",
479
+ "php": ">=5.6.0"
480
+ },
481
+ "require-dev": {
482
+ "phpunit/phpunit": "~5.7.10|~6.5|~7.0"
483
+ },
484
+ "time": "2018-10-02T21:52:37+00:00",
485
+ "type": "library",
486
+ "extra": {
487
+ "branch-alias": {
488
+ "dev-master": "1.0.x-dev"
489
+ }
490
+ },
491
+ "installation-source": "dist",
492
+ "autoload": {
493
+ "psr-0": {
494
+ "Mockery": "library/"
495
+ }
496
+ },
497
+ "notification-url": "https://packagist.org/downloads/",
498
+ "license": [
499
+ "BSD-3-Clause"
500
+ ],
501
+ "authors": [
502
+ {
503
+ "name": "Pádraic Brady",
504
+ "email": "padraic.brady@gmail.com",
505
+ "homepage": "http://blog.astrumfutura.com"
506
+ },
507
+ {
508
+ "name": "Dave Marshall",
509
+ "email": "dave.marshall@atstsolutions.co.uk",
510
+ "homepage": "http://davedevelopment.co.uk"
511
+ }
512
+ ],
513
+ "description": "Mockery is a simple yet flexible PHP mock object framework",
514
+ "homepage": "https://github.com/mockery/mockery",
515
+ "keywords": [
516
+ "BDD",
517
+ "TDD",
518
+ "library",
519
+ "mock",
520
+ "mock objects",
521
+ "mockery",
522
+ "stub",
523
+ "test",
524
+ "test double",
525
+ "testing"
526
+ ]
527
+ },
528
+ {
529
+ "name": "myclabs/deep-copy",
530
+ "version": "1.8.1",
531
+ "version_normalized": "1.8.1.0",
532
+ "source": {
533
+ "type": "git",
534
+ "url": "https://github.com/myclabs/DeepCopy.git",
535
+ "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8"
536
+ },
537
+ "dist": {
538
+ "type": "zip",
539
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8",
540
+ "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8",
541
+ "shasum": ""
542
+ },
543
+ "require": {
544
+ "php": "^7.1"
545
+ },
546
+ "replace": {
547
+ "myclabs/deep-copy": "self.version"
548
+ },
549
+ "require-dev": {
550
+ "doctrine/collections": "^1.0",
551
+ "doctrine/common": "^2.6",
552
+ "phpunit/phpunit": "^7.1"
553
+ },
554
+ "time": "2018-06-11T23:09:50+00:00",
555
+ "type": "library",
556
+ "installation-source": "dist",
557
+ "autoload": {
558
+ "psr-4": {
559
+ "DeepCopy\\": "src/DeepCopy/"
560
+ },
561
+ "files": [
562
+ "src/DeepCopy/deep_copy.php"
563
+ ]
564
+ },
565
+ "notification-url": "https://packagist.org/downloads/",
566
+ "license": [
567
+ "MIT"
568
+ ],
569
+ "description": "Create deep copies (clones) of your objects",
570
+ "keywords": [
571
+ "clone",
572
+ "copy",
573
+ "duplicate",
574
+ "object",
575
+ "object graph"
576
+ ]
577
+ },
578
+ {
579
+ "name": "netresearch/jsonmapper",
580
+ "version": "v1.4.0",
581
+ "version_normalized": "1.4.0.0",
582
+ "source": {
583
+ "type": "git",
584
+ "url": "https://github.com/cweiske/jsonmapper.git",
585
+ "reference": "3868fe1128ce1169228acdb623359dca74db5ef3"
586
+ },
587
+ "dist": {
588
+ "type": "zip",
589
+ "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/3868fe1128ce1169228acdb623359dca74db5ef3",
590
+ "reference": "3868fe1128ce1169228acdb623359dca74db5ef3",
591
+ "shasum": ""
592
+ },
593
+ "require": {
594
+ "php": ">=5.6"
595
+ },
596
+ "require-dev": {
597
+ "phpunit/phpunit": "~4.8.35 || ~5.7 || ~6.4",
598
+ "squizlabs/php_codesniffer": "~1.5"
599
+ },
600
+ "time": "2017-11-28T21:30:01+00:00",
601
+ "type": "library",
602
+ "installation-source": "dist",
603
+ "autoload": {
604
+ "psr-0": {
605
+ "JsonMapper": "src/"
606
+ }
607
+ },
608
+ "notification-url": "https://packagist.org/downloads/",
609
+ "license": [
610
+ "OSL-3.0"
611
+ ],
612
+ "authors": [
613
+ {
614
+ "name": "Christian Weiske",
615
+ "email": "cweiske@cweiske.de",
616
+ "homepage": "http://github.com/cweiske/jsonmapper/",
617
+ "role": "Developer"
618
+ }
619
+ ],
620
+ "description": "Map nested JSON structures onto PHP classes"
621
+ },
622
+ {
623
+ "name": "phan/phan",
624
+ "version": "1.2.0",
625
+ "version_normalized": "1.2.0.0",
626
+ "source": {
627
+ "type": "git",
628
+ "url": "https://github.com/phan/phan.git",
629
+ "reference": "e961f8e1458055488795adf3701535562baa526e"
630
+ },
631
+ "dist": {
632
+ "type": "zip",
633
+ "url": "https://api.github.com/repos/phan/phan/zipball/e961f8e1458055488795adf3701535562baa526e",
634
+ "reference": "e961f8e1458055488795adf3701535562baa526e",
635
+ "shasum": ""
636
+ },
637
+ "require": {
638
+ "composer/semver": "^1.4",
639
+ "composer/xdebug-handler": "^1.3",
640
+ "ext-filter": "*",
641
+ "ext-json": "*",
642
+ "felixfbecker/advanced-json-rpc": "^3.0.3",
643
+ "microsoft/tolerant-php-parser": "0.0.15",
644
+ "php": "^7.0.0",
645
+ "sabre/event": "^5.0",
646
+ "symfony/console": "^2.3|^3.0|~4.0"
647
+ },
648
+ "require-dev": {
649
+ "phpunit/phpunit": "^6.3.0"
650
+ },
651
+ "suggest": {
652
+ "ext-ast": "Needed for parsing ASTs (unless --use-fallback-parser is used). php-ast ^0.1.5|^1.0.0 is needed.",
653
+ "ext-tokenizer": "Needed for non-AST support and file/line-based suppressions."
654
+ },
655
+ "time": "2019-01-05T15:41:52+00:00",
656
+ "bin": [
657
+ "phan",
658
+ "phan_client",
659
+ "tocheckstyle"
660
+ ],
661
+ "type": "project",
662
+ "installation-source": "dist",
663
+ "autoload": {
664
+ "psr-4": {
665
+ "Phan\\": "src/Phan"
666
+ }
667
+ },
668
+ "notification-url": "https://packagist.org/downloads/",
669
+ "license": [
670
+ "MIT"
671
+ ],
672
+ "authors": [
673
+ {
674
+ "name": "Rasmus Lerdorf"
675
+ },
676
+ {
677
+ "name": "Andrew S. Morrison"
678
+ },
679
+ {
680
+ "name": "Tyson Andre"
681
+ }
682
+ ],
683
+ "description": "A static analyzer for PHP",
684
+ "keywords": [
685
+ "analyzer",
686
+ "php",
687
+ "static"
688
+ ]
689
+ },
690
+ {
691
+ "name": "phpcompatibility/php-compatibility",
692
+ "version": "9.1.1",
693
+ "version_normalized": "9.1.1.0",
694
+ "source": {
695
+ "type": "git",
696
+ "url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
697
+ "reference": "2b63c5d284ab8857f7b1d5c240ddb507a6b2293c"
698
+ },
699
+ "dist": {
700
+ "type": "zip",
701
+ "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/2b63c5d284ab8857f7b1d5c240ddb507a6b2293c",
702
+ "reference": "2b63c5d284ab8857f7b1d5c240ddb507a6b2293c",
703
+ "shasum": ""
704
+ },
705
+ "require": {
706
+ "php": ">=5.3",
707
+ "squizlabs/php_codesniffer": "^2.3 || ^3.0.2"
708
+ },
709
+ "conflict": {
710
+ "squizlabs/php_codesniffer": "2.6.2"
711
+ },
712
+ "require-dev": {
713
+ "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0"
714
+ },
715
+ "suggest": {
716
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.",
717
+ "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
718
+ },
719
+ "time": "2018-12-30T23:16:27+00:00",
720
+ "type": "phpcodesniffer-standard",
721
+ "installation-source": "dist",
722
+ "notification-url": "https://packagist.org/downloads/",
723
+ "license": [
724
+ "LGPL-3.0-or-later"
725
+ ],
726
+ "authors": [
727
+ {
728
+ "name": "Contributors",
729
+ "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors"
730
+ },
731
+ {
732
+ "name": "Wim Godden",
733
+ "homepage": "https://github.com/wimg",
734
+ "role": "lead"
735
+ },
736
+ {
737
+ "name": "Juliette Reinders Folmer",
738
+ "homepage": "https://github.com/jrfnl",
739
+ "role": "lead"
740
+ }
741
+ ],
742
+ "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.",
743
+ "homepage": "http://techblog.wimgodden.be/tag/codesniffer/",
744
+ "keywords": [
745
+ "compatibility",
746
+ "phpcs",
747
+ "standards"
748
+ ]
749
+ },
750
+ {
751
+ "name": "phpcompatibility/phpcompatibility-paragonie",
752
+ "version": "1.0.1",
753
+ "version_normalized": "1.0.1.0",
754
+ "source": {
755
+ "type": "git",
756
+ "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git",
757
+ "reference": "9160de79fcd683b5c99e9c4133728d91529753ea"
758
+ },
759
+ "dist": {
760
+ "type": "zip",
761
+ "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/9160de79fcd683b5c99e9c4133728d91529753ea",
762
+ "reference": "9160de79fcd683b5c99e9c4133728d91529753ea",
763
+ "shasum": ""
764
+ },
765
+ "require": {
766
+ "phpcompatibility/php-compatibility": "^9.0"
767
+ },
768
+ "require-dev": {
769
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4"
770
+ },
771
+ "suggest": {
772
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
773
+ "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
774
+ },
775
+ "time": "2018-12-16T19:10:44+00:00",
776
+ "type": "phpcodesniffer-standard",
777
+ "installation-source": "dist",
778
+ "notification-url": "https://packagist.org/downloads/",
779
+ "license": [
780
+ "LGPL-3.0-or-later"
781
+ ],
782
+ "authors": [
783
+ {
784
+ "name": "Wim Godden",
785
+ "role": "lead"
786
+ },
787
+ {
788
+ "name": "Juliette Reinders Folmer",
789
+ "role": "lead"
790
+ }
791
+ ],
792
+ "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.",
793
+ "homepage": "http://phpcompatibility.com/",
794
+ "keywords": [
795
+ "compatibility",
796
+ "paragonie",
797
+ "phpcs",
798
+ "polyfill",
799
+ "standards"
800
+ ]
801
+ },
802
+ {
803
+ "name": "phpcompatibility/phpcompatibility-wp",
804
+ "version": "2.0.0",
805
+ "version_normalized": "2.0.0.0",
806
+ "source": {
807
+ "type": "git",
808
+ "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git",
809
+ "reference": "cb303f0067cd5b366a41d4fb0e254fb40ff02efd"
810
+ },
811
+ "dist": {
812
+ "type": "zip",
813
+ "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/cb303f0067cd5b366a41d4fb0e254fb40ff02efd",
814
+ "reference": "cb303f0067cd5b366a41d4fb0e254fb40ff02efd",
815
+ "shasum": ""
816
+ },
817
+ "require": {
818
+ "phpcompatibility/php-compatibility": "^9.0",
819
+ "phpcompatibility/phpcompatibility-paragonie": "^1.0"
820
+ },
821
+ "require-dev": {
822
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3"
823
+ },
824
+ "suggest": {
825
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
826
+ "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
827
+ },
828
+ "time": "2018-10-07T18:31:37+00:00",
829
+ "type": "phpcodesniffer-standard",
830
+ "installation-source": "dist",
831
+ "notification-url": "https://packagist.org/downloads/",
832
+ "license": [
833
+ "LGPL-3.0-or-later"
834
+ ],
835
+ "authors": [
836
+ {
837
+ "name": "Wim Godden",
838
+ "role": "lead"
839
+ },
840
+ {
841
+ "name": "Juliette Reinders Folmer",
842
+ "role": "lead"
843
+ }
844
+ ],
845
+ "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.",
846
+ "homepage": "http://phpcompatibility.com/",
847
+ "keywords": [
848
+ "compatibility",
849
+ "phpcs",
850
+ "standards",
851
+ "wordpress"
852
+ ]
853
+ },
854
+ {
855
+ "name": "phpdocumentor/reflection-common",
856
+ "version": "1.0.1",
857
+ "version_normalized": "1.0.1.0",
858
+ "source": {
859
+ "type": "git",
860
+ "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
861
+ "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
862
+ },
863
+ "dist": {
864
+ "type": "zip",
865
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
866
+ "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
867
+ "shasum": ""
868
+ },
869
+ "require": {
870
+ "php": ">=5.5"
871
+ },
872
+ "require-dev": {
873
+ "phpunit/phpunit": "^4.6"
874
+ },
875
+ "time": "2017-09-11T18:02:19+00:00",
876
+ "type": "library",
877
+ "extra": {
878
+ "branch-alias": {
879
+ "dev-master": "1.0.x-dev"
880
+ }
881
+ },
882
+ "installation-source": "dist",
883
+ "autoload": {
884
+ "psr-4": {
885
+ "phpDocumentor\\Reflection\\": [
886
+ "src"
887
+ ]
888
+ }
889
+ },
890
+ "notification-url": "https://packagist.org/downloads/",
891
+ "license": [
892
+ "MIT"
893
+ ],
894
+ "authors": [
895
+ {
896
+ "name": "Jaap van Otterdijk",
897
+ "email": "opensource@ijaap.nl"
898
+ }
899
+ ],
900
+ "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
901
+ "homepage": "http://www.phpdoc.org",
902
+ "keywords": [
903
+ "FQSEN",
904
+ "phpDocumentor",
905
+ "phpdoc",
906
+ "reflection",
907
+ "static analysis"
908
+ ]
909
+ },
910
+ {
911
+ "name": "phpdocumentor/reflection-docblock",
912
+ "version": "4.3.0",
913
+ "version_normalized": "4.3.0.0",
914
+ "source": {
915
+ "type": "git",
916
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
917
+ "reference": "94fd0001232e47129dd3504189fa1c7225010d08"
918
+ },
919
+ "dist": {
920
+ "type": "zip",
921
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08",
922
+ "reference": "94fd0001232e47129dd3504189fa1c7225010d08",
923
+ "shasum": ""
924
+ },
925
+ "require": {
926
+ "php": "^7.0",
927
+ "phpdocumentor/reflection-common": "^1.0.0",
928
+ "phpdocumentor/type-resolver": "^0.4.0",
929
+ "webmozart/assert": "^1.0"
930
+ },
931
+ "require-dev": {
932
+ "doctrine/instantiator": "~1.0.5",
933
+ "mockery/mockery": "^1.0",
934
+ "phpunit/phpunit": "^6.4"
935
+ },
936
+ "time": "2017-11-30T07:14:17+00:00",
937
+ "type": "library",
938
+ "extra": {
939
+ "branch-alias": {
940
+ "dev-master": "4.x-dev"
941
+ }
942
+ },
943
+ "installation-source": "dist",
944
+ "autoload": {
945
+ "psr-4": {
946
+ "phpDocumentor\\Reflection\\": [
947
+ "src/"
948
+ ]
949
+ }
950
+ },
951
+ "notification-url": "https://packagist.org/downloads/",
952
+ "license": [
953
+ "MIT"
954
+ ],
955
+ "authors": [
956
+ {
957
+ "name": "Mike van Riel",
958
+ "email": "me@mikevanriel.com"
959
+ }
960
+ ],
961
+ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock."
962
+ },
963
+ {
964
+ "name": "phpdocumentor/type-resolver",
965
+ "version": "0.4.0",
966
+ "version_normalized": "0.4.0.0",
967
+ "source": {
968
+ "type": "git",
969
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
970
+ "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
971
+ },
972
+ "dist": {
973
+ "type": "zip",
974
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
975
+ "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
976
+ "shasum": ""
977
+ },
978
+ "require": {
979
+ "php": "^5.5 || ^7.0",
980
+ "phpdocumentor/reflection-common": "^1.0"
981
+ },
982
+ "require-dev": {
983
+ "mockery/mockery": "^0.9.4",
984
+ "phpunit/phpunit": "^5.2||^4.8.24"
985
+ },
986
+ "time": "2017-07-14T14:27:02+00:00",
987
+ "type": "library",
988
+ "extra": {
989
+ "branch-alias": {
990
+ "dev-master": "1.0.x-dev"
991
+ }
992
+ },
993
+ "installation-source": "dist",
994
+ "autoload": {
995
+ "psr-4": {
996
+ "phpDocumentor\\Reflection\\": [
997
+ "src/"
998
+ ]
999
+ }
1000
+ },
1001
+ "notification-url": "https://packagist.org/downloads/",
1002
+ "license": [
1003
+ "MIT"
1004
+ ],
1005
+ "authors": [
1006
+ {
1007
+ "name": "Mike van Riel",
1008
+ "email": "me@mikevanriel.com"
1009
+ }
1010
+ ]
1011
+ },
1012
+ {
1013
+ "name": "phpspec/prophecy",
1014
+ "version": "1.8.0",
1015
+ "version_normalized": "1.8.0.0",
1016
+ "source": {
1017
+ "type": "git",
1018
+ "url": "https://github.com/phpspec/prophecy.git",
1019
+ "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06"
1020
+ },
1021
+ "dist": {
1022
+ "type": "zip",
1023
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
1024
+ "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
1025
+ "shasum": ""
1026
+ },
1027
+ "require": {
1028
+ "doctrine/instantiator": "^1.0.2",
1029
+ "php": "^5.3|^7.0",
1030
+ "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
1031
+ "sebastian/comparator": "^1.1|^2.0|^3.0",
1032
+ "sebastian/recursion-context": "^1.0|^2.0|^3.0"
1033
+ },
1034
+ "require-dev": {
1035
+ "phpspec/phpspec": "^2.5|^3.2",
1036
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
1037
+ },
1038
+ "time": "2018-08-05T17:53:17+00:00",
1039
+ "type": "library",
1040
+ "extra": {
1041
+ "branch-alias": {
1042
+ "dev-master": "1.8.x-dev"
1043
+ }
1044
+ },
1045
+ "installation-source": "dist",
1046
+ "autoload": {
1047
+ "psr-0": {
1048
+ "Prophecy\\": "src/"
1049
+ }
1050
+ },
1051
+ "notification-url": "https://packagist.org/downloads/",
1052
+ "license": [
1053
+ "MIT"
1054
+ ],
1055
+ "authors": [
1056
+ {
1057
+ "name": "Konstantin Kudryashov",
1058
+ "email": "ever.zet@gmail.com",
1059
+ "homepage": "http://everzet.com"
1060
+ },
1061
+ {
1062
+ "name": "Marcello Duarte",
1063
+ "email": "marcello.duarte@gmail.com"
1064
+ }
1065
+ ],
1066
+ "description": "Highly opinionated mocking framework for PHP 5.3+",
1067
+ "homepage": "https://github.com/phpspec/prophecy",
1068
+ "keywords": [
1069
+ "Double",
1070
+ "Dummy",
1071
+ "fake",
1072
+ "mock",
1073
+ "spy",
1074
+ "stub"
1075
+ ]
1076
+ },
1077
+ {
1078
+ "name": "phpunit/php-code-coverage",
1079
+ "version": "4.0.8",
1080
+ "version_normalized": "4.0.8.0",
1081
+ "source": {
1082
+ "type": "git",
1083
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
1084
+ "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d"
1085
+ },
1086
+ "dist": {
1087
+ "type": "zip",
1088
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d",
1089
+ "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d",
1090
+ "shasum": ""
1091
+ },
1092
+ "require": {
1093
+ "ext-dom": "*",
1094
+ "ext-xmlwriter": "*",
1095
+ "php": "^5.6 || ^7.0",
1096
+ "phpunit/php-file-iterator": "^1.3",
1097
+ "phpunit/php-text-template": "^1.2",
1098
+ "phpunit/php-token-stream": "^1.4.2 || ^2.0",
1099
+ "sebastian/code-unit-reverse-lookup": "^1.0",
1100
+ "sebastian/environment": "^1.3.2 || ^2.0",
1101
+ "sebastian/version": "^1.0 || ^2.0"
1102
+ },
1103
+ "require-dev": {
1104
+ "ext-xdebug": "^2.1.4",
1105
+ "phpunit/phpunit": "^5.7"
1106
+ },
1107
+ "suggest": {
1108
+ "ext-xdebug": "^2.5.1"
1109
+ },
1110
+ "time": "2017-04-02T07:44:40+00:00",
1111
+ "type": "library",
1112
+ "extra": {
1113
+ "branch-alias": {
1114
+ "dev-master": "4.0.x-dev"
1115
+ }
1116
+ },
1117
+ "installation-source": "dist",
1118
+ "autoload": {
1119
+ "classmap": [
1120
+ "src/"
1121
+ ]
1122
+ },
1123
+ "notification-url": "https://packagist.org/downloads/",
1124
+ "license": [
1125
+ "BSD-3-Clause"
1126
+ ],
1127
+ "authors": [
1128
+ {
1129
+ "name": "Sebastian Bergmann",
1130
+ "email": "sb@sebastian-bergmann.de",
1131
+ "role": "lead"
1132
+ }
1133
+ ],
1134
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
1135
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
1136
+ "keywords": [
1137
+ "coverage",
1138
+ "testing",
1139
+ "xunit"
1140
+ ]
1141
+ },
1142
+ {
1143
+ "name": "phpunit/php-file-iterator",
1144
+ "version": "1.4.5",
1145
+ "version_normalized": "1.4.5.0",
1146
+ "source": {
1147
+ "type": "git",
1148
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
1149
+ "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4"
1150
+ },
1151
+ "dist": {
1152
+ "type": "zip",
1153
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
1154
+ "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4",
1155
+ "shasum": ""
1156
+ },
1157
+ "require": {
1158
+ "php": ">=5.3.3"
1159
+ },
1160
+ "time": "2017-11-27T13:52:08+00:00",
1161
+ "type": "library",
1162
+ "extra": {
1163
+ "branch-alias": {
1164
+ "dev-master": "1.4.x-dev"
1165
+ }
1166
+ },
1167
+ "installation-source": "dist",
1168
+ "autoload": {
1169
+ "classmap": [
1170
+ "src/"
1171
+ ]
1172
+ },
1173
+ "notification-url": "https://packagist.org/downloads/",
1174
+ "license": [
1175
+ "BSD-3-Clause"
1176
+ ],
1177
+ "authors": [
1178
+ {
1179
+ "name": "Sebastian Bergmann",
1180
+ "email": "sb@sebastian-bergmann.de",
1181
+ "role": "lead"
1182
+ }
1183
+ ],
1184
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
1185
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
1186
+ "keywords": [
1187
+ "filesystem",
1188
+ "iterator"
1189
+ ]
1190
+ },
1191
+ {
1192
+ "name": "phpunit/php-text-template",
1193
+ "version": "1.2.1",
1194
+ "version_normalized": "1.2.1.0",
1195
+ "source": {
1196
+ "type": "git",
1197
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
1198
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
1199
+ },
1200
+ "dist": {
1201
+ "type": "zip",
1202
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
1203
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
1204
+ "shasum": ""
1205
+ },
1206
+ "require": {
1207
+ "php": ">=5.3.3"
1208
+ },
1209
+ "time": "2015-06-21T13:50:34+00:00",
1210
+ "type": "library",
1211
+ "installation-source": "dist",
1212
+ "autoload": {
1213
+ "classmap": [
1214
+ "src/"
1215
+ ]
1216
+ },
1217
+ "notification-url": "https://packagist.org/downloads/",
1218
+ "license": [
1219
+ "BSD-3-Clause"
1220
+ ],
1221
+ "authors": [
1222
+ {
1223
+ "name": "Sebastian Bergmann",
1224
+ "email": "sebastian@phpunit.de",
1225
+ "role": "lead"
1226
+ }
1227
+ ],
1228
+ "description": "Simple template engine.",
1229
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
1230
+ "keywords": [
1231
+ "template"
1232
+ ]
1233
+ },
1234
+ {
1235
+ "name": "phpunit/php-timer",
1236
+ "version": "1.0.9",
1237
+ "version_normalized": "1.0.9.0",
1238
+ "source": {
1239
+ "type": "git",
1240
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
1241
+ "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
1242
+ },
1243
+ "dist": {
1244
+ "type": "zip",
1245
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
1246
+ "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
1247
+ "shasum": ""
1248
+ },
1249
+ "require": {
1250
+ "php": "^5.3.3 || ^7.0"
1251
+ },
1252
+ "require-dev": {
1253
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
1254
+ },
1255
+ "time": "2017-02-26T11:10:40+00:00",
1256
+ "type": "library",
1257
+ "extra": {
1258
+ "branch-alias": {
1259
+ "dev-master": "1.0-dev"
1260
+ }
1261
+ },
1262
+ "installation-source": "dist",
1263
+ "autoload": {
1264
+ "classmap": [
1265
+ "src/"
1266
+ ]
1267
+ },
1268
+ "notification-url": "https://packagist.org/downloads/",
1269
+ "license": [
1270
+ "BSD-3-Clause"
1271
+ ],
1272
+ "authors": [
1273
+ {
1274
+ "name": "Sebastian Bergmann",
1275
+ "email": "sb@sebastian-bergmann.de",
1276
+ "role": "lead"
1277
+ }
1278
+ ],
1279
+ "description": "Utility class for timing",
1280
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
1281
+ "keywords": [
1282
+ "timer"
1283
+ ]
1284
+ },
1285
+ {
1286
+ "name": "phpunit/php-token-stream",
1287
+ "version": "2.0.2",
1288
+ "version_normalized": "2.0.2.0",
1289
+ "source": {
1290
+ "type": "git",
1291
+ "url": "https://github.com/sebastianbergmann/php-token-stream.git",
1292
+ "reference": "791198a2c6254db10131eecfe8c06670700904db"
1293
+ },
1294
+ "dist": {
1295
+ "type": "zip",
1296
+ "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db",
1297
+ "reference": "791198a2c6254db10131eecfe8c06670700904db",
1298
+ "shasum": ""
1299
+ },
1300
+ "require": {
1301
+ "ext-tokenizer": "*",
1302
+ "php": "^7.0"
1303
+ },
1304
+ "require-dev": {
1305
+ "phpunit/phpunit": "^6.2.4"
1306
+ },
1307
+ "time": "2017-11-27T05:48:46+00:00",
1308
+ "type": "library",
1309
+ "extra": {
1310
+ "branch-alias": {
1311
+ "dev-master": "2.0-dev"
1312
+ }
1313
+ },
1314
+ "installation-source": "dist",
1315
+ "autoload": {
1316
+ "classmap": [
1317
+ "src/"
1318
+ ]
1319
+ },
1320
+ "notification-url": "https://packagist.org/downloads/",
1321
+ "license": [
1322
+ "BSD-3-Clause"
1323
+ ],
1324
+ "authors": [
1325
+ {
1326
+ "name": "Sebastian Bergmann",
1327
+ "email": "sebastian@phpunit.de"
1328
+ }
1329
+ ],
1330
+ "description": "Wrapper around PHP's tokenizer extension.",
1331
+ "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
1332
+ "keywords": [
1333
+ "tokenizer"
1334
+ ]
1335
+ },
1336
+ {
1337
+ "name": "phpunit/phpunit",
1338
+ "version": "5.7.27",
1339
+ "version_normalized": "5.7.27.0",
1340
+ "source": {
1341
+ "type": "git",
1342
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
1343
+ "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c"
1344
+ },
1345
+ "dist": {
1346
+ "type": "zip",
1347
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c",
1348
+ "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c",
1349
+ "shasum": ""
1350
+ },
1351
+ "require": {
1352
+ "ext-dom": "*",
1353
+ "ext-json": "*",
1354
+ "ext-libxml": "*",
1355
+ "ext-mbstring": "*",
1356
+ "ext-xml": "*",
1357
+ "myclabs/deep-copy": "~1.3",
1358
+ "php": "^5.6 || ^7.0",
1359
+ "phpspec/prophecy": "^1.6.2",
1360
+ "phpunit/php-code-coverage": "^4.0.4",
1361
+ "phpunit/php-file-iterator": "~1.4",
1362
+ "phpunit/php-text-template": "~1.2",
1363
+ "phpunit/php-timer": "^1.0.6",
1364
+ "phpunit/phpunit-mock-objects": "^3.2",
1365
+ "sebastian/comparator": "^1.2.4",
1366
+ "sebastian/diff": "^1.4.3",
1367
+ "sebastian/environment": "^1.3.4 || ^2.0",
1368
+ "sebastian/exporter": "~2.0",
1369
+ "sebastian/global-state": "^1.1",
1370
+ "sebastian/object-enumerator": "~2.0",
1371
+ "sebastian/resource-operations": "~1.0",
1372
+ "sebastian/version": "^1.0.6|^2.0.1",
1373
+ "symfony/yaml": "~2.1|~3.0|~4.0"
1374
+ },
1375
+ "conflict": {
1376
+ "phpdocumentor/reflection-docblock": "3.0.2"
1377
+ },
1378
+ "require-dev": {
1379
+ "ext-pdo": "*"
1380
+ },
1381
+ "suggest": {
1382
+ "ext-xdebug": "*",
1383
+ "phpunit/php-invoker": "~1.1"
1384
+ },
1385
+ "time": "2018-02-01T05:50:59+00:00",
1386
+ "bin": [
1387
+ "phpunit"
1388
+ ],
1389
+ "type": "library",
1390
+ "extra": {
1391
+ "branch-alias": {
1392
+ "dev-master": "5.7.x-dev"
1393
+ }
1394
+ },
1395
+ "installation-source": "dist",
1396
+ "autoload": {
1397
+ "classmap": [
1398
+ "src/"
1399
+ ]
1400
+ },
1401
+ "notification-url": "https://packagist.org/downloads/",
1402
+ "license": [
1403
+ "BSD-3-Clause"
1404
+ ],
1405
+ "authors": [
1406
+ {
1407
+ "name": "Sebastian Bergmann",
1408
+ "email": "sebastian@phpunit.de",
1409
+ "role": "lead"
1410
+ }
1411
+ ],
1412
+ "description": "The PHP Unit Testing framework.",
1413
+ "homepage": "https://phpunit.de/",
1414
+ "keywords": [
1415
+ "phpunit",
1416
+ "testing",
1417
+ "xunit"
1418
+ ]
1419
+ },
1420
+ {
1421
+ "name": "phpunit/phpunit-mock-objects",
1422
+ "version": "3.4.4",
1423
+ "version_normalized": "3.4.4.0",
1424
+ "source": {
1425
+ "type": "git",
1426
+ "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
1427
+ "reference": "a23b761686d50a560cc56233b9ecf49597cc9118"
1428
+ },
1429
+ "dist": {
1430
+ "type": "zip",
1431
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118",
1432
+ "reference": "a23b761686d50a560cc56233b9ecf49597cc9118",
1433
+ "shasum": ""
1434
+ },
1435
+ "require": {
1436
+ "doctrine/instantiator": "^1.0.2",
1437
+ "php": "^5.6 || ^7.0",
1438
+ "phpunit/php-text-template": "^1.2",
1439
+ "sebastian/exporter": "^1.2 || ^2.0"
1440
+ },
1441
+ "conflict": {
1442
+ "phpunit/phpunit": "<5.4.0"
1443
+ },
1444
+ "require-dev": {
1445
+ "phpunit/phpunit": "^5.4"
1446
+ },
1447
+ "suggest": {
1448
+ "ext-soap": "*"
1449
+ },
1450
+ "time": "2017-06-30T09:13:00+00:00",
1451
+ "type": "library",
1452
+ "extra": {
1453
+ "branch-alias": {
1454
+ "dev-master": "3.2.x-dev"
1455
+ }
1456
+ },
1457
+ "installation-source": "dist",
1458
+ "autoload": {
1459
+ "classmap": [
1460
+ "src/"
1461
+ ]
1462
+ },
1463
+ "notification-url": "https://packagist.org/downloads/",
1464
+ "license": [
1465
+ "BSD-3-Clause"
1466
+ ],
1467
+ "authors": [
1468
+ {
1469
+ "name": "Sebastian Bergmann",
1470
+ "email": "sb@sebastian-bergmann.de",
1471
+ "role": "lead"
1472
+ }
1473
+ ],
1474
+ "description": "Mock Object library for PHPUnit",
1475
+ "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
1476
+ "keywords": [
1477
+ "mock",
1478
+ "xunit"
1479
+ ]
1480
+ },
1481
+ {
1482
+ "name": "psr/log",
1483
+ "version": "1.1.0",
1484
+ "version_normalized": "1.1.0.0",
1485
+ "source": {
1486
+ "type": "git",
1487
+ "url": "https://github.com/php-fig/log.git",
1488
+ "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
1489
+ },
1490
+ "dist": {
1491
+ "type": "zip",
1492
+ "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
1493
+ "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
1494
+ "shasum": ""
1495
+ },
1496
+ "require": {
1497
+ "php": ">=5.3.0"
1498
+ },
1499
+ "time": "2018-11-20T15:27:04+00:00",
1500
+ "type": "library",
1501
+ "extra": {
1502
+ "branch-alias": {
1503
+ "dev-master": "1.0.x-dev"
1504
+ }
1505
+ },
1506
+ "installation-source": "dist",
1507
+ "autoload": {
1508
+ "psr-4": {
1509
+ "Psr\\Log\\": "Psr/Log/"
1510
+ }
1511
+ },
1512
+ "notification-url": "https://packagist.org/downloads/",
1513
+ "license": [
1514
+ "MIT"
1515
+ ],
1516
+ "authors": [
1517
+ {
1518
+ "name": "PHP-FIG",
1519
+ "homepage": "http://www.php-fig.org/"
1520
+ }
1521
+ ],
1522
+ "description": "Common interface for logging libraries",
1523
+ "homepage": "https://github.com/php-fig/log",
1524
+ "keywords": [
1525
+ "log",
1526
+ "psr",
1527
+ "psr-3"
1528
+ ]
1529
+ },
1530
+ {
1531
+ "name": "sabre/event",
1532
+ "version": "5.0.3",
1533
+ "version_normalized": "5.0.3.0",
1534
+ "source": {
1535
+ "type": "git",
1536
+ "url": "https://github.com/sabre-io/event.git",
1537
+ "reference": "f5cf802d240df1257866d8813282b98aee3bc548"
1538
+ },
1539
+ "dist": {
1540
+ "type": "zip",
1541
+ "url": "https://api.github.com/repos/sabre-io/event/zipball/f5cf802d240df1257866d8813282b98aee3bc548",
1542
+ "reference": "f5cf802d240df1257866d8813282b98aee3bc548",
1543
+ "shasum": ""
1544
+ },
1545
+ "require": {
1546
+ "php": ">=7.0"
1547
+ },
1548
+ "require-dev": {
1549
+ "phpunit/phpunit": ">=6",
1550
+ "sabre/cs": "~1.0.0"
1551
+ },
1552
+ "time": "2018-03-05T13:55:47+00:00",
1553
+ "type": "library",
1554
+ "installation-source": "dist",
1555
+ "autoload": {
1556
+ "psr-4": {
1557
+ "Sabre\\Event\\": "lib/"
1558
+ },
1559
+ "files": [
1560
+ "lib/coroutine.php",
1561
+ "lib/Loop/functions.php",
1562
+ "lib/Promise/functions.php"
1563
+ ]
1564
+ },
1565
+ "notification-url": "https://packagist.org/downloads/",
1566
+ "license": [
1567
+ "BSD-3-Clause"
1568
+ ],
1569
+ "authors": [
1570
+ {
1571
+ "name": "Evert Pot",
1572
+ "email": "me@evertpot.com",
1573
+ "homepage": "http://evertpot.com/",
1574
+ "role": "Developer"
1575
+ }
1576
+ ],
1577
+ "description": "sabre/event is a library for lightweight event-based programming",
1578
+ "homepage": "http://sabre.io/event/",
1579
+ "keywords": [
1580
+ "EventEmitter",
1581
+ "async",
1582
+ "coroutine",
1583
+ "eventloop",
1584
+ "events",
1585
+ "hooks",
1586
+ "plugin",
1587
+ "promise",
1588
+ "reactor",
1589
+ "signal"
1590
+ ]
1591
+ },
1592
+ {
1593
+ "name": "sebastian/code-unit-reverse-lookup",
1594
+ "version": "1.0.1",
1595
+ "version_normalized": "1.0.1.0",
1596
+ "source": {
1597
+ "type": "git",
1598
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
1599
+ "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
1600
+ },
1601
+ "dist": {
1602
+ "type": "zip",
1603
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
1604
+ "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
1605
+ "shasum": ""
1606
+ },
1607
+ "require": {
1608
+ "php": "^5.6 || ^7.0"
1609
+ },
1610
+ "require-dev": {
1611
+ "phpunit/phpunit": "^5.7 || ^6.0"
1612
+ },
1613
+ "time": "2017-03-04T06:30:41+00:00",
1614
+ "type": "library",
1615
+ "extra": {
1616
+ "branch-alias": {
1617
+ "dev-master": "1.0.x-dev"
1618
+ }
1619
+ },
1620
+ "installation-source": "dist",
1621
+ "autoload": {
1622
+ "classmap": [
1623
+ "src/"
1624
+ ]
1625
+ },
1626
+ "notification-url": "https://packagist.org/downloads/",
1627
+ "license": [
1628
+ "BSD-3-Clause"
1629
+ ],
1630
+ "authors": [
1631
+ {
1632
+ "name": "Sebastian Bergmann",
1633
+ "email": "sebastian@phpunit.de"
1634
+ }
1635
+ ],
1636
+ "description": "Looks up which function or method a line of code belongs to",
1637
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/"
1638
+ },
1639
+ {
1640
+ "name": "sebastian/comparator",
1641
+ "version": "1.2.4",
1642
+ "version_normalized": "1.2.4.0",
1643
+ "source": {
1644
+ "type": "git",
1645
+ "url": "https://github.com/sebastianbergmann/comparator.git",
1646
+ "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be"
1647
+ },
1648
+ "dist": {
1649
+ "type": "zip",
1650
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
1651
+ "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
1652
+ "shasum": ""
1653
+ },
1654
+ "require": {
1655
+ "php": ">=5.3.3",
1656
+ "sebastian/diff": "~1.2",
1657
+ "sebastian/exporter": "~1.2 || ~2.0"
1658
+ },
1659
+ "require-dev": {
1660
+ "phpunit/phpunit": "~4.4"
1661
+ },
1662
+ "time": "2017-01-29T09:50:25+00:00",
1663
+ "type": "library",
1664
+ "extra": {
1665
+ "branch-alias": {
1666
+ "dev-master": "1.2.x-dev"
1667
+ }
1668
+ },
1669
+ "installation-source": "dist",
1670
+ "autoload": {
1671
+ "classmap": [
1672
+ "src/"
1673
+ ]
1674
+ },
1675
+ "notification-url": "https://packagist.org/downloads/",
1676
+ "license": [
1677
+ "BSD-3-Clause"
1678
+ ],
1679
+ "authors": [
1680
+ {
1681
+ "name": "Jeff Welch",
1682
+ "email": "whatthejeff@gmail.com"
1683
+ },
1684
+ {
1685
+ "name": "Volker Dusch",
1686
+ "email": "github@wallbash.com"
1687
+ },
1688
+ {
1689
+ "name": "Bernhard Schussek",
1690
+ "email": "bschussek@2bepublished.at"
1691
+ },
1692
+ {
1693
+ "name": "Sebastian Bergmann",
1694
+ "email": "sebastian@phpunit.de"
1695
+ }
1696
+ ],
1697
+ "description": "Provides the functionality to compare PHP values for equality",
1698
+ "homepage": "http://www.github.com/sebastianbergmann/comparator",
1699
+ "keywords": [
1700
+ "comparator",
1701
+ "compare",
1702
+ "equality"
1703
+ ]
1704
+ },
1705
+ {
1706
+ "name": "sebastian/diff",
1707
+ "version": "1.4.3",
1708
+ "version_normalized": "1.4.3.0",
1709
+ "source": {
1710
+ "type": "git",
1711
+ "url": "https://github.com/sebastianbergmann/diff.git",
1712
+ "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4"
1713
+ },
1714
+ "dist": {
1715
+ "type": "zip",
1716
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4",
1717
+ "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4",
1718
+ "shasum": ""
1719
+ },
1720
+ "require": {
1721
+ "php": "^5.3.3 || ^7.0"
1722
+ },
1723
+ "require-dev": {
1724
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
1725
+ },
1726
+ "time": "2017-05-22T07:24:03+00:00",
1727
+ "type": "library",
1728
+ "extra": {
1729
+ "branch-alias": {
1730
+ "dev-master": "1.4-dev"
1731
+ }
1732
+ },
1733
+ "installation-source": "dist",
1734
+ "autoload": {
1735
+ "classmap": [
1736
+ "src/"
1737
+ ]
1738
+ },
1739
+ "notification-url": "https://packagist.org/downloads/",
1740
+ "license": [
1741
+ "BSD-3-Clause"
1742
+ ],
1743
+ "authors": [
1744
+ {
1745
+ "name": "Kore Nordmann",
1746
+ "email": "mail@kore-nordmann.de"
1747
+ },
1748
+ {
1749
+ "name": "Sebastian Bergmann",
1750
+ "email": "sebastian@phpunit.de"
1751
+ }
1752
+ ],
1753
+ "description": "Diff implementation",
1754
+ "homepage": "https://github.com/sebastianbergmann/diff",
1755
+ "keywords": [
1756
+ "diff"
1757
+ ]
1758
+ },
1759
+ {
1760
+ "name": "sebastian/environment",
1761
+ "version": "2.0.0",
1762
+ "version_normalized": "2.0.0.0",
1763
+ "source": {
1764
+ "type": "git",
1765
+ "url": "https://github.com/sebastianbergmann/environment.git",
1766
+ "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac"
1767
+ },
1768
+ "dist": {
1769
+ "type": "zip",
1770
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac",
1771
+ "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac",
1772
+ "shasum": ""
1773
+ },
1774
+ "require": {
1775
+ "php": "^5.6 || ^7.0"
1776
+ },
1777
+ "require-dev": {
1778
+ "phpunit/phpunit": "^5.0"
1779
+ },
1780
+ "time": "2016-11-26T07:53:53+00:00",
1781
+ "type": "library",
1782
+ "extra": {
1783
+ "branch-alias": {
1784
+ "dev-master": "2.0.x-dev"
1785
+ }
1786
+ },
1787
+ "installation-source": "dist",
1788
+ "autoload": {
1789
+ "classmap": [
1790
+ "src/"
1791
+ ]
1792
+ },
1793
+ "notification-url": "https://packagist.org/downloads/",
1794
+ "license": [
1795
+ "BSD-3-Clause"
1796
+ ],
1797
+ "authors": [
1798
+ {
1799
+ "name": "Sebastian Bergmann",
1800
+ "email": "sebastian@phpunit.de"
1801
+ }
1802
+ ],
1803
+ "description": "Provides functionality to handle HHVM/PHP environments",
1804
+ "homepage": "http://www.github.com/sebastianbergmann/environment",
1805
+ "keywords": [
1806
+ "Xdebug",
1807
+ "environment",
1808
+ "hhvm"
1809
+ ]
1810
+ },
1811
+ {
1812
+ "name": "sebastian/exporter",
1813
+ "version": "2.0.0",
1814
+ "version_normalized": "2.0.0.0",
1815
+ "source": {
1816
+ "type": "git",
1817
+ "url": "https://github.com/sebastianbergmann/exporter.git",
1818
+ "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4"
1819
+ },
1820
+ "dist": {
1821
+ "type": "zip",
1822
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4",
1823
+ "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4",
1824
+ "shasum": ""
1825
+ },
1826
+ "require": {
1827
+ "php": ">=5.3.3",
1828
+ "sebastian/recursion-context": "~2.0"
1829
+ },
1830
+ "require-dev": {
1831
+ "ext-mbstring": "*",
1832
+ "phpunit/phpunit": "~4.4"
1833
+ },
1834
+ "time": "2016-11-19T08:54:04+00:00",
1835
+ "type": "library",
1836
+ "extra": {
1837
+ "branch-alias": {
1838
+ "dev-master": "2.0.x-dev"
1839
+ }
1840
+ },
1841
+ "installation-source": "dist",
1842
+ "autoload": {
1843
+ "classmap": [
1844
+ "src/"
1845
+ ]
1846
+ },
1847
+ "notification-url": "https://packagist.org/downloads/",
1848
+ "license": [
1849
+ "BSD-3-Clause"
1850
+ ],
1851
+ "authors": [
1852
+ {
1853
+ "name": "Jeff Welch",
1854
+ "email": "whatthejeff@gmail.com"
1855
+ },
1856
+ {
1857
+ "name": "Volker Dusch",
1858
+ "email": "github@wallbash.com"
1859
+ },
1860
+ {
1861
+ "name": "Bernhard Schussek",
1862
+ "email": "bschussek@2bepublished.at"
1863
+ },
1864
+ {
1865
+ "name": "Sebastian Bergmann",
1866
+ "email": "sebastian@phpunit.de"
1867
+ },
1868
+ {
1869
+ "name": "Adam Harvey",
1870
+ "email": "aharvey@php.net"
1871
+ }
1872
+ ],
1873
+ "description": "Provides the functionality to export PHP variables for visualization",
1874
+ "homepage": "http://www.github.com/sebastianbergmann/exporter",
1875
+ "keywords": [
1876
+ "export",
1877
+ "exporter"
1878
+ ]
1879
+ },
1880
+ {
1881
+ "name": "sebastian/global-state",
1882
+ "version": "1.1.1",
1883
+ "version_normalized": "1.1.1.0",
1884
+ "source": {
1885
+ "type": "git",
1886
+ "url": "https://github.com/sebastianbergmann/global-state.git",
1887
+ "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
1888
+ },
1889
+ "dist": {
1890
+ "type": "zip",
1891
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
1892
+ "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
1893
+ "shasum": ""
1894
+ },
1895
+ "require": {
1896
+ "php": ">=5.3.3"
1897
+ },
1898
+ "require-dev": {
1899
+ "phpunit/phpunit": "~4.2"
1900
+ },
1901
+ "suggest": {
1902
+ "ext-uopz": "*"
1903
+ },
1904
+ "time": "2015-10-12T03:26:01+00:00",
1905
+ "type": "library",
1906
+ "extra": {
1907
+ "branch-alias": {
1908
+ "dev-master": "1.0-dev"
1909
+ }
1910
+ },
1911
+ "installation-source": "dist",
1912
+ "autoload": {
1913
+ "classmap": [
1914
+ "src/"
1915
+ ]
1916
+ },
1917
+ "notification-url": "https://packagist.org/downloads/",
1918
+ "license": [
1919
+ "BSD-3-Clause"
1920
+ ],
1921
+ "authors": [
1922
+ {
1923
+ "name": "Sebastian Bergmann",
1924
+ "email": "sebastian@phpunit.de"
1925
+ }
1926
+ ],
1927
+ "description": "Snapshotting of global state",
1928
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
1929
+ "keywords": [
1930
+ "global state"
1931
+ ]
1932
+ },
1933
+ {
1934
+ "name": "sebastian/object-enumerator",
1935
+ "version": "2.0.1",
1936
+ "version_normalized": "2.0.1.0",
1937
+ "source": {
1938
+ "type": "git",
1939
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
1940
+ "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7"
1941
+ },
1942
+ "dist": {
1943
+ "type": "zip",
1944
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7",
1945
+ "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7",
1946
+ "shasum": ""
1947
+ },
1948
+ "require": {
1949
+ "php": ">=5.6",
1950
+ "sebastian/recursion-context": "~2.0"
1951
+ },
1952
+ "require-dev": {
1953
+ "phpunit/phpunit": "~5"
1954
+ },
1955
+ "time": "2017-02-18T15:18:39+00:00",
1956
+ "type": "library",
1957
+ "extra": {
1958
+ "branch-alias": {
1959
+ "dev-master": "2.0.x-dev"
1960
+ }
1961
+ },
1962
+ "installation-source": "dist",
1963
+ "autoload": {
1964
+ "classmap": [
1965
+ "src/"
1966
+ ]
1967
+ },
1968
+ "notification-url": "https://packagist.org/downloads/",
1969
+ "license": [
1970
+ "BSD-3-Clause"
1971
+ ],
1972
+ "authors": [
1973
+ {
1974
+ "name": "Sebastian Bergmann",
1975
+ "email": "sebastian@phpunit.de"
1976
+ }
1977
+ ],
1978
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
1979
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/"
1980
+ },
1981
+ {
1982
+ "name": "sebastian/recursion-context",
1983
+ "version": "2.0.0",
1984
+ "version_normalized": "2.0.0.0",
1985
+ "source": {
1986
+ "type": "git",
1987
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
1988
+ "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a"
1989
+ },
1990
+ "dist": {
1991
+ "type": "zip",
1992
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a",
1993
+ "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a",
1994
+ "shasum": ""
1995
+ },
1996
+ "require": {
1997
+ "php": ">=5.3.3"
1998
+ },
1999
+ "require-dev": {
2000
+ "phpunit/phpunit": "~4.4"
2001
+ },
2002
+ "time": "2016-11-19T07:33:16+00:00",
2003
+ "type": "library",
2004
+ "extra": {
2005
+ "branch-alias": {
2006
+ "dev-master": "2.0.x-dev"
2007
+ }
2008
+ },
2009
+ "installation-source": "dist",
2010
+ "autoload": {
2011
+ "classmap": [
2012
+ "src/"
2013
+ ]
2014
+ },
2015
+ "notification-url": "https://packagist.org/downloads/",
2016
+ "license": [
2017
+ "BSD-3-Clause"
2018
+ ],
2019
+ "authors": [
2020
+ {
2021
+ "name": "Jeff Welch",
2022
+ "email": "whatthejeff@gmail.com"
2023
+ },
2024
+ {
2025
+ "name": "Sebastian Bergmann",
2026
+ "email": "sebastian@phpunit.de"
2027
+ },
2028
+ {
2029
+ "name": "Adam Harvey",
2030
+ "email": "aharvey@php.net"
2031
+ }
2032
+ ],
2033
+ "description": "Provides functionality to recursively process PHP variables",
2034
+ "homepage": "http://www.github.com/sebastianbergmann/recursion-context"
2035
+ },
2036
+ {
2037
+ "name": "sebastian/resource-operations",
2038
+ "version": "1.0.0",
2039
+ "version_normalized": "1.0.0.0",
2040
+ "source": {
2041
+ "type": "git",
2042
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
2043
+ "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
2044
+ },
2045
+ "dist": {
2046
+ "type": "zip",
2047
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
2048
+ "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
2049
+ "shasum": ""
2050
+ },
2051
+ "require": {
2052
+ "php": ">=5.6.0"
2053
+ },
2054
+ "time": "2015-07-28T20:34:47+00:00",
2055
+ "type": "library",
2056
+ "extra": {
2057
+ "branch-alias": {
2058
+ "dev-master": "1.0.x-dev"
2059
+ }
2060
+ },
2061
+ "installation-source": "dist",
2062
+ "autoload": {
2063
+ "classmap": [
2064
+ "src/"
2065
+ ]
2066
+ },
2067
+ "notification-url": "https://packagist.org/downloads/",
2068
+ "license": [
2069
+ "BSD-3-Clause"
2070
+ ],
2071
+ "authors": [
2072
+ {
2073
+ "name": "Sebastian Bergmann",
2074
+ "email": "sebastian@phpunit.de"
2075
+ }
2076
+ ],
2077
+ "description": "Provides a list of PHP built-in functions that operate on resources",
2078
+ "homepage": "https://www.github.com/sebastianbergmann/resource-operations"
2079
+ },
2080
+ {
2081
+ "name": "sebastian/version",
2082
+ "version": "2.0.1",
2083
+ "version_normalized": "2.0.1.0",
2084
+ "source": {
2085
+ "type": "git",
2086
+ "url": "https://github.com/sebastianbergmann/version.git",
2087
+ "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
2088
+ },
2089
+ "dist": {
2090
+ "type": "zip",
2091
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
2092
+ "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
2093
+ "shasum": ""
2094
+ },
2095
+ "require": {
2096
+ "php": ">=5.6"
2097
+ },
2098
+ "time": "2016-10-03T07:35:21+00:00",
2099
+ "type": "library",
2100
+ "extra": {
2101
+ "branch-alias": {
2102
+ "dev-master": "2.0.x-dev"
2103
+ }
2104
+ },
2105
+ "installation-source": "dist",
2106
+ "autoload": {
2107
+ "classmap": [
2108
+ "src/"
2109
+ ]
2110
+ },
2111
+ "notification-url": "https://packagist.org/downloads/",
2112
+ "license": [
2113
+ "BSD-3-Clause"
2114
+ ],
2115
+ "authors": [
2116
+ {
2117
+ "name": "Sebastian Bergmann",
2118
+ "email": "sebastian@phpunit.de",
2119
+ "role": "lead"
2120
+ }
2121
+ ],
2122
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
2123
+ "homepage": "https://github.com/sebastianbergmann/version"
2124
+ },
2125
+ {
2126
+ "name": "squizlabs/php_codesniffer",
2127
+ "version": "3.4.0",
2128
+ "version_normalized": "3.4.0.0",
2129
+ "source": {
2130
+ "type": "git",
2131
+ "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
2132
+ "reference": "379deb987e26c7cd103a7b387aea178baec96e48"
2133
+ },
2134
+ "dist": {
2135
+ "type": "zip",
2136
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/379deb987e26c7cd103a7b387aea178baec96e48",
2137
+ "reference": "379deb987e26c7cd103a7b387aea178baec96e48",
2138
+ "shasum": ""
2139
+ },
2140
+ "require": {
2141
+ "ext-simplexml": "*",
2142
+ "ext-tokenizer": "*",
2143
+ "ext-xmlwriter": "*",
2144
+ "php": ">=5.4.0"
2145
+ },
2146
+ "require-dev": {
2147
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
2148
+ },
2149
+ "time": "2018-12-19T23:57:18+00:00",
2150
+ "bin": [
2151
+ "bin/phpcs",
2152
+ "bin/phpcbf"
2153
+ ],
2154
+ "type": "library",
2155
+ "extra": {
2156
+ "branch-alias": {
2157
+ "dev-master": "3.x-dev"
2158
+ }
2159
+ },
2160
+ "installation-source": "dist",
2161
+ "notification-url": "https://packagist.org/downloads/",
2162
+ "license": [
2163
+ "BSD-3-Clause"
2164
+ ],
2165
+ "authors": [
2166
+ {
2167
+ "name": "Greg Sherwood",
2168
+ "role": "lead"
2169
+ }
2170
+ ],
2171
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
2172
+ "homepage": "http://www.squizlabs.com/php-codesniffer",
2173
+ "keywords": [
2174
+ "phpcs",
2175
+ "standards"
2176
+ ]
2177
+ },
2178
+ {
2179
+ "name": "symfony/console",
2180
+ "version": "v4.2.2",
2181
+ "version_normalized": "4.2.2.0",
2182
+ "source": {
2183
+ "type": "git",
2184
+ "url": "https://github.com/symfony/console.git",
2185
+ "reference": "b0a03c1bb0fcbe288629956cf2f1dd3f1dc97522"
2186
+ },
2187
+ "dist": {
2188
+ "type": "zip",
2189
+ "url": "https://api.github.com/repos/symfony/console/zipball/b0a03c1bb0fcbe288629956cf2f1dd3f1dc97522",
2190
+ "reference": "b0a03c1bb0fcbe288629956cf2f1dd3f1dc97522",
2191
+ "shasum": ""
2192
+ },
2193
+ "require": {
2194
+ "php": "^7.1.3",
2195
+ "symfony/contracts": "^1.0",
2196
+ "symfony/polyfill-mbstring": "~1.0"
2197
+ },
2198
+ "conflict": {
2199
+ "symfony/dependency-injection": "<3.4",
2200
+ "symfony/process": "<3.3"
2201
+ },
2202
+ "require-dev": {
2203
+ "psr/log": "~1.0",
2204
+ "symfony/config": "~3.4|~4.0",
2205
+ "symfony/dependency-injection": "~3.4|~4.0",
2206
+ "symfony/event-dispatcher": "~3.4|~4.0",
2207
+ "symfony/lock": "~3.4|~4.0",
2208
+ "symfony/process": "~3.4|~4.0"
2209
+ },
2210
+ "suggest": {
2211
+ "psr/log-implementation": "For using the console logger",
2212
+ "symfony/event-dispatcher": "",
2213
+ "symfony/lock": "",
2214
+ "symfony/process": ""
2215
+ },
2216
+ "time": "2019-01-04T15:13:53+00:00",
2217
+ "type": "library",
2218
+ "extra": {
2219
+ "branch-alias": {
2220
+ "dev-master": "4.2-dev"
2221
+ }
2222
+ },
2223
+ "installation-source": "dist",
2224
+ "autoload": {
2225
+ "psr-4": {
2226
+ "Symfony\\Component\\Console\\": ""
2227
+ },
2228
+ "exclude-from-classmap": [
2229
+ "/Tests/"
2230
+ ]
2231
+ },
2232
+ "notification-url": "https://packagist.org/downloads/",
2233
+ "license": [
2234
+ "MIT"
2235
+ ],
2236
+ "authors": [
2237
+ {
2238
+ "name": "Fabien Potencier",
2239
+ "email": "fabien@symfony.com"
2240
+ },
2241
+ {
2242
+ "name": "Symfony Community",
2243
+ "homepage": "https://symfony.com/contributors"
2244
+ }
2245
+ ],
2246
+ "description": "Symfony Console Component",
2247
+ "homepage": "https://symfony.com"
2248
+ },
2249
+ {
2250
+ "name": "symfony/contracts",
2251
+ "version": "v1.0.2",
2252
+ "version_normalized": "1.0.2.0",
2253
+ "source": {
2254
+ "type": "git",
2255
+ "url": "https://github.com/symfony/contracts.git",
2256
+ "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf"
2257
+ },
2258
+ "dist": {
2259
+ "type": "zip",
2260
+ "url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf",
2261
+ "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf",
2262
+ "shasum": ""
2263
+ },
2264
+ "require": {
2265
+ "php": "^7.1.3"
2266
+ },
2267
+ "require-dev": {
2268
+ "psr/cache": "^1.0",
2269
+ "psr/container": "^1.0"
2270
+ },
2271
+ "suggest": {
2272
+ "psr/cache": "When using the Cache contracts",
2273
+ "psr/container": "When using the Service contracts",
2274
+ "symfony/cache-contracts-implementation": "",
2275
+ "symfony/service-contracts-implementation": "",
2276
+ "symfony/translation-contracts-implementation": ""
2277
+ },
2278
+ "time": "2018-12-05T08:06:11+00:00",
2279
+ "type": "library",
2280
+ "extra": {
2281
+ "branch-alias": {
2282
+ "dev-master": "1.0-dev"
2283
+ }
2284
+ },
2285
+ "installation-source": "dist",
2286
+ "autoload": {
2287
+ "psr-4": {
2288
+ "Symfony\\Contracts\\": ""
2289
+ },
2290
+ "exclude-from-classmap": [
2291
+ "**/Tests/"
2292
+ ]
2293
+ },
2294
+ "notification-url": "https://packagist.org/downloads/",
2295
+ "license": [
2296
+ "MIT"
2297
+ ],
2298
+ "authors": [
2299
+ {
2300
+ "name": "Nicolas Grekas",
2301
+ "email": "p@tchwork.com"
2302
+ },
2303
+ {
2304
+ "name": "Symfony Community",
2305
+ "homepage": "https://symfony.com/contributors"
2306
+ }
2307
+ ],
2308
+ "description": "A set of abstractions extracted out of the Symfony components",
2309
+ "homepage": "https://symfony.com",
2310
+ "keywords": [
2311
+ "abstractions",
2312
+ "contracts",
2313
+ "decoupling",
2314
+ "interfaces",
2315
+ "interoperability",
2316
+ "standards"
2317
+ ]
2318
+ },
2319
+ {
2320
+ "name": "symfony/polyfill-ctype",
2321
+ "version": "v1.10.0",
2322
+ "version_normalized": "1.10.0.0",
2323
+ "source": {
2324
+ "type": "git",
2325
+ "url": "https://github.com/symfony/polyfill-ctype.git",
2326
+ "reference": "e3d826245268269cd66f8326bd8bc066687b4a19"
2327
+ },
2328
+ "dist": {
2329
+ "type": "zip",
2330
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19",
2331
+ "reference": "e3d826245268269cd66f8326bd8bc066687b4a19",
2332
+ "shasum": ""
2333
+ },
2334
+ "require": {
2335
+ "php": ">=5.3.3"
2336
+ },
2337
+ "suggest": {
2338
+ "ext-ctype": "For best performance"
2339
+ },
2340
+ "time": "2018-08-06T14:22:27+00:00",
2341
+ "type": "library",
2342
+ "extra": {
2343
+ "branch-alias": {
2344
+ "dev-master": "1.9-dev"
2345
+ }
2346
+ },
2347
+ "installation-source": "dist",
2348
+ "autoload": {
2349
+ "psr-4": {
2350
+ "Symfony\\Polyfill\\Ctype\\": ""
2351
+ },
2352
+ "files": [
2353
+ "bootstrap.php"
2354
+ ]
2355
+ },
2356
+ "notification-url": "https://packagist.org/downloads/",
2357
+ "license": [
2358
+ "MIT"
2359
+ ],
2360
+ "authors": [
2361
+ {
2362
+ "name": "Symfony Community",
2363
+ "homepage": "https://symfony.com/contributors"
2364
+ },
2365
+ {
2366
+ "name": "Gert de Pagter",
2367
+ "email": "BackEndTea@gmail.com"
2368
+ }
2369
+ ],
2370
+ "description": "Symfony polyfill for ctype functions",
2371
+ "homepage": "https://symfony.com",
2372
+ "keywords": [
2373
+ "compatibility",
2374
+ "ctype",
2375
+ "polyfill",
2376
+ "portable"
2377
+ ]
2378
+ },
2379
+ {
2380
+ "name": "symfony/polyfill-mbstring",
2381
+ "version": "v1.10.0",
2382
+ "version_normalized": "1.10.0.0",
2383
+ "source": {
2384
+ "type": "git",
2385
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
2386
+ "reference": "c79c051f5b3a46be09205c73b80b346e4153e494"
2387
+ },
2388
+ "dist": {
2389
+ "type": "zip",
2390
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494",
2391
+ "reference": "c79c051f5b3a46be09205c73b80b346e4153e494",
2392
+ "shasum": ""
2393
+ },
2394
+ "require": {
2395
+ "php": ">=5.3.3"
2396
+ },
2397
+ "suggest": {
2398
+ "ext-mbstring": "For best performance"
2399
+ },
2400
+ "time": "2018-09-21T13:07:52+00:00",
2401
+ "type": "library",
2402
+ "extra": {
2403
+ "branch-alias": {
2404
+ "dev-master": "1.9-dev"
2405
+ }
2406
+ },
2407
+ "installation-source": "dist",
2408
+ "autoload": {
2409
+ "psr-4": {
2410
+ "Symfony\\Polyfill\\Mbstring\\": ""
2411
+ },
2412
+ "files": [
2413
+ "bootstrap.php"
2414
+ ]
2415
+ },
2416
+ "notification-url": "https://packagist.org/downloads/",
2417
+ "license": [
2418
+ "MIT"
2419
+ ],
2420
+ "authors": [
2421
+ {
2422
+ "name": "Nicolas Grekas",
2423
+ "email": "p@tchwork.com"
2424
+ },
2425
+ {
2426
+ "name": "Symfony Community",
2427
+ "homepage": "https://symfony.com/contributors"
2428
+ }
2429
+ ],
2430
+ "description": "Symfony polyfill for the Mbstring extension",
2431
+ "homepage": "https://symfony.com",
2432
+ "keywords": [
2433
+ "compatibility",
2434
+ "mbstring",
2435
+ "polyfill",
2436
+ "portable",
2437
+ "shim"
2438
+ ]
2439
+ },
2440
+ {
2441
+ "name": "symfony/yaml",
2442
+ "version": "v4.2.2",
2443
+ "version_normalized": "4.2.2.0",
2444
+ "source": {
2445
+ "type": "git",
2446
+ "url": "https://github.com/symfony/yaml.git",
2447
+ "reference": "d0aa6c0ea484087927b49fd513383a7d36190ca6"
2448
+ },
2449
+ "dist": {
2450
+ "type": "zip",
2451
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/d0aa6c0ea484087927b49fd513383a7d36190ca6",
2452
+ "reference": "d0aa6c0ea484087927b49fd513383a7d36190ca6",
2453
+ "shasum": ""
2454
+ },
2455
+ "require": {
2456
+ "php": "^7.1.3",
2457
+ "symfony/polyfill-ctype": "~1.8"
2458
+ },
2459
+ "conflict": {
2460
+ "symfony/console": "<3.4"
2461
+ },
2462
+ "require-dev": {
2463
+ "symfony/console": "~3.4|~4.0"
2464
+ },
2465
+ "suggest": {
2466
+ "symfony/console": "For validating YAML files using the lint command"
2467
+ },
2468
+ "time": "2019-01-03T09:07:35+00:00",
2469
+ "type": "library",
2470
+ "extra": {
2471
+ "branch-alias": {
2472
+ "dev-master": "4.2-dev"
2473
+ }
2474
+ },
2475
+ "installation-source": "dist",
2476
+ "autoload": {
2477
+ "psr-4": {
2478
+ "Symfony\\Component\\Yaml\\": ""
2479
+ },
2480
+ "exclude-from-classmap": [
2481
+ "/Tests/"
2482
+ ]
2483
+ },
2484
+ "notification-url": "https://packagist.org/downloads/",
2485
+ "license": [
2486
+ "MIT"
2487
+ ],
2488
+ "authors": [
2489
+ {
2490
+ "name": "Fabien Potencier",
2491
+ "email": "fabien@symfony.com"
2492
+ },
2493
+ {
2494
+ "name": "Symfony Community",
2495
+ "homepage": "https://symfony.com/contributors"
2496
+ }
2497
+ ],
2498
+ "description": "Symfony Yaml Component",
2499
+ "homepage": "https://symfony.com"
2500
+ },
2501
+ {
2502
+ "name": "webmozart/assert",
2503
+ "version": "1.4.0",
2504
+ "version_normalized": "1.4.0.0",
2505
+ "source": {
2506
+ "type": "git",
2507
+ "url": "https://github.com/webmozart/assert.git",
2508
+ "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
2509
+ },
2510
+ "dist": {
2511
+ "type": "zip",
2512
+ "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
2513
+ "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
2514
+ "shasum": ""
2515
+ },
2516
+ "require": {
2517
+ "php": "^5.3.3 || ^7.0",
2518
+ "symfony/polyfill-ctype": "^1.8"
2519
+ },
2520
+ "require-dev": {
2521
+ "phpunit/phpunit": "^4.6",
2522
+ "sebastian/version": "^1.0.1"
2523
+ },
2524
+ "time": "2018-12-25T11:19:39+00:00",
2525
+ "type": "library",
2526
+ "extra": {
2527
+ "branch-alias": {
2528
+ "dev-master": "1.3-dev"
2529
+ }
2530
+ },
2531
+ "installation-source": "dist",
2532
+ "autoload": {
2533
+ "psr-4": {
2534
+ "Webmozart\\Assert\\": "src/"
2535
+ }
2536
+ },
2537
+ "notification-url": "https://packagist.org/downloads/",
2538
+ "license": [
2539
+ "MIT"
2540
+ ],
2541
+ "authors": [
2542
+ {
2543
+ "name": "Bernhard Schussek",
2544
+ "email": "bschussek@gmail.com"
2545
+ }
2546
+ ],
2547
+ "description": "Assertions to validate method input/output with nice error messages.",
2548
+ "keywords": [
2549
+ "assert",
2550
+ "check",
2551
+ "validate"
2552
+ ]
2553
+ },
2554
+ {
2555
+ "name": "wp-coding-standards/wpcs",
2556
+ "version": "2.0.0",
2557
+ "version_normalized": "2.0.0.0",
2558
+ "source": {
2559
+ "type": "git",
2560
+ "url": "https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards.git",
2561
+ "reference": "c9eaadaafefce36b3cb7e06eb15305b8c4cae9ce"
2562
+ },
2563
+ "dist": {
2564
+ "type": "zip",
2565
+ "url": "https://api.github.com/repos/WordPress-Coding-Standards/WordPress-Coding-Standards/zipball/c9eaadaafefce36b3cb7e06eb15305b8c4cae9ce",
2566
+ "reference": "c9eaadaafefce36b3cb7e06eb15305b8c4cae9ce",
2567
+ "shasum": ""
2568
+ },
2569
+ "require": {
2570
+ "php": ">=5.4",
2571
+ "squizlabs/php_codesniffer": "^3.3.1"
2572
+ },
2573
+ "require-dev": {
2574
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0",
2575
+ "phpcompatibility/php-compatibility": "^9.0",
2576
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
2577
+ },
2578
+ "suggest": {
2579
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically."
2580
+ },
2581
+ "time": "2019-01-16T10:13:16+00:00",
2582
+ "type": "phpcodesniffer-standard",
2583
+ "installation-source": "dist",
2584
+ "notification-url": "https://packagist.org/downloads/",
2585
+ "license": [
2586
+ "MIT"
2587
+ ],
2588
+ "authors": [
2589
+ {
2590
+ "name": "Contributors",
2591
+ "homepage": "https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/graphs/contributors"
2592
+ }
2593
+ ],
2594
+ "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
2595
+ "keywords": [
2596
+ "phpcs",
2597
+ "standards",
2598
+ "wordpress"
2599
+ ]
2600
+ }
2601
+ ]
vendor/composer/semver/LICENSE ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (C) 2015 Composer
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
vendor/composer/semver/composer.json ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "composer/semver",
3
+ "description": "Semver library that offers utilities, version constraint parsing and validation.",
4
+ "type": "library",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "semver",
8
+ "semantic",
9
+ "versioning",
10
+ "validation"
11
+ ],
12
+ "authors": [
13
+ {
14
+ "name": "Nils Adermann",
15
+ "email": "naderman@naderman.de",
16
+ "homepage": "http://www.naderman.de"
17
+ },
18
+ {
19
+ "name": "Jordi Boggiano",
20
+ "email": "j.boggiano@seld.be",
21
+ "homepage": "http://seld.be"
22
+ },
23
+ {
24
+ "name": "Rob Bast",
25
+ "email": "rob.bast@gmail.com",
26
+ "homepage": "http://robbast.nl"
27
+ }
28
+ ],
29
+ "support": {
30
+ "irc": "irc://irc.freenode.org/composer",
31
+ "issues": "https://github.com/composer/semver/issues"
32
+ },
33
+ "require": {
34
+ "php": "^5.3.2 || ^7.0"
35
+ },
36
+ "require-dev": {
37
+ "phpunit/phpunit": "^4.5 || ^5.0.5",
38
+ "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
39
+ },
40
+ "autoload": {
41
+ "psr-4": {
42
+ "Composer\\Semver\\": "src"
43
+ }
44
+ },
45
+ "autoload-dev": {
46
+ "psr-4": {
47
+ "Composer\\Semver\\": "tests"
48
+ }
49
+ },
50
+ "extra": {
51
+ "branch-alias": {
52
+ "dev-master": "1.x-dev"
53
+ }
54
+ },
55
+ "scripts": {
56
+ "test": "phpunit"
57
+ }
58
+ }
vendor/composer/semver/src/Comparator.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of composer/semver.
5
+ *
6
+ * (c) Composer <https://github.com/composer>
7
+ *
8
+ * For the full copyright and license information, please view
9
+ * the LICENSE file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Composer\Semver;
13
+
14
+ use Composer\Semver\Constraint\Constraint;
15
+
16
+ class Comparator
17
+ {
18
+ /**
19
+ * Evaluates the expression: $version1 > $version2.
20
+ *
21
+ * @param string $version1
22
+ * @param string $version2
23
+ *
24
+ * @return bool
25
+ */
26
+ public static function greaterThan($version1, $version2)
27
+ {
28
+ return self::compare($version1, '>', $version2);
29
+ }
30
+
31
+ /**
32
+ * Evaluates the expression: $version1 >= $version2.
33
+ *
34
+ * @param string $version1
35
+ * @param string $version2
36
+ *
37
+ * @return bool
38
+ */
39
+ public static function greaterThanOrEqualTo($version1, $version2)
40
+ {
41
+ return self::compare($version1, '>=', $version2);
42
+ }
43
+
44
+ /**
45
+ * Evaluates the expression: $version1 < $version2.
46
+ *
47
+ * @param string $version1
48
+ * @param string $version2
49
+ *
50
+ * @return bool
51
+ */
52
+ public static function lessThan($version1, $version2)
53
+ {
54
+ return self::compare($version1, '<', $version2);
55
+ }
56
+
57
+ /**
58
+ * Evaluates the expression: $version1 <= $version2.
59
+ *
60
+ * @param string $version1
61
+ * @param string $version2
62
+ *
63
+ * @return bool
64
+ */
65
+ public static function lessThanOrEqualTo($version1, $version2)
66
+ {
67
+ return self::compare($version1, '<=', $version2);
68
+ }
69
+
70
+ /**
71
+ * Evaluates the expression: $version1 == $version2.
72
+ *
73
+ * @param string $version1
74
+ * @param string $version2
75
+ *
76
+ * @return bool
77
+ */
78
+ public static function equalTo($version1, $version2)
79
+ {
80
+ return self::compare($version1, '==', $version2);
81
+ }
82
+
83
+ /**
84
+ * Evaluates the expression: $version1 != $version2.
85
+ *
86
+ * @param string $version1
87
+ * @param string $version2
88
+ *
89
+ * @return bool
90
+ */
91
+ public static function notEqualTo($version1, $version2)
92
+ {
93
+ return self::compare($version1, '!=', $version2);
94
+ }
95
+
96
+ /**
97
+ * Evaluates the expression: $version1 $operator $version2.
98
+ *
99
+ * @param string $version1
100
+ * @param string $operator
101
+ * @param string $version2
102
+ *
103
+ * @return bool
104
+ */
105
+ public static function compare($version1, $operator, $version2)
106
+ {
107
+ $constraint = new Constraint($operator, $version2);
108
+
109
+ return $constraint->matches(new Constraint('==', $version1));
110
+ }
111
+ }
vendor/composer/semver/src/Constraint/AbstractConstraint.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of composer/semver.
5
+ *
6
+ * (c) Composer <https://github.com/composer>
7
+ *
8
+ * For the full copyright and license information, please view
9
+ * the LICENSE file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Composer\Semver\Constraint;
13
+
14
+ trigger_error('The ' . __CLASS__ . ' abstract class is deprecated, there is no replacement for it, it will be removed in the next major version.', E_USER_DEPRECATED);
15
+
16
+ /**
17
+ * Base constraint class.
18
+ */
19
+ abstract class AbstractConstraint implements ConstraintInterface
20
+ {
21
+ /** @var string */
22
+ protected $prettyString;
23
+
24
+ /**
25
+ * @param ConstraintInterface $provider
26
+ *
27
+ * @return bool
28
+ */
29
+ public function matches(ConstraintInterface $provider)
30
+ {
31
+ if ($provider instanceof $this) {
32
+ // see note at bottom of this class declaration
33
+ return $this->matchSpecific($provider);
34
+ }
35
+
36
+ // turn matching around to find a match
37
+ return $provider->matches($this);
38
+ }
39
+
40
+ /**
41
+ * @param string $prettyString
42
+ */
43
+ public function setPrettyString($prettyString)
44
+ {
45
+ $this->prettyString = $prettyString;
46
+ }
47
+
48
+ /**
49
+ * @return string
50
+ */
51
+ public function getPrettyString()
52
+ {
53
+ if ($this->prettyString) {
54
+ return $this->prettyString;
55
+ }
56
+
57
+ return $this->__toString();
58
+ }
59
+
60
+ // implementations must implement a method of this format:
61
+ // not declared abstract here because type hinting violates parameter coherence (TODO right word?)
62
+ // public function matchSpecific(<SpecificConstraintType> $provider);
63
+ }
vendor/composer/semver/src/Constraint/Constraint.php ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of composer/semver.
5
+ *
6
+ * (c) Composer <https://github.com/composer>
7
+ *
8
+ * For the full copyright and license information, please view
9
+ * the LICENSE file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Composer\Semver\Constraint;
13
+
14
+ /**
15
+ * Defines a constraint.
16
+ */
17
+ class Constraint implements ConstraintInterface
18
+ {
19
+ /* operator integer values */
20
+ const OP_EQ = 0;
21
+ const OP_LT = 1;
22
+ const OP_LE = 2;
23
+ const OP_GT = 3;
24
+ const OP_GE = 4;
25
+ const OP_NE = 5;
26
+
27
+ /**
28
+ * Operator to integer translation table.
29
+ *
30
+ * @var array
31
+ */
32
+ private static $transOpStr = array(
33
+ '=' => self::OP_EQ,
34
+ '==' => self::OP_EQ,
35
+ '<' => self::OP_LT,
36
+ '<=' => self::OP_LE,
37
+ '>' => self::OP_GT,
38
+ '>=' => self::OP_GE,
39
+ '<>' => self::OP_NE,
40
+ '!=' => self::OP_NE,
41
+ );
42
+
43
+ /**
44
+ * Integer to operator translation table.
45
+ *
46
+ * @var array
47
+ */
48
+ private static $transOpInt = array(
49
+ self::OP_EQ => '==',
50
+ self::OP_LT => '<',
51
+ self::OP_LE => '<=',
52
+ self::OP_GT => '>',
53
+ self::OP_GE => '>=',
54
+ self::OP_NE => '!=',
55
+ );
56
+
57
+ /** @var string */
58
+ protected $operator;
59
+
60
+ /** @var string */
61
+ protected $version;
62
+
63
+ /** @var string */
64
+ protected $prettyString;
65
+
66
+ /**
67
+ * @param ConstraintInterface $provider
68
+ *
69
+ * @return bool
70
+ */
71
+ public function matches(ConstraintInterface $provider)
72
+ {
73
+ if ($provider instanceof $this) {
74
+ return $this->matchSpecific($provider);
75
+ }
76
+
77
+ // turn matching around to find a match
78
+ return $provider->matches($this);
79
+ }
80
+
81
+ /**
82
+ * @param string $prettyString
83
+ */
84
+ public function setPrettyString($prettyString)
85
+ {
86
+ $this->prettyString = $prettyString;
87
+ }
88
+
89
+ /**
90
+ * @return string
91
+ */
92
+ public function getPrettyString()
93
+ {
94
+ if ($this->prettyString) {
95
+ return $this->prettyString;
96
+ }
97
+
98
+ return $this->__toString();
99
+ }
100
+
101
+ /**
102
+ * Get all supported comparison operators.
103
+ *
104
+ * @return array
105
+ */
106
+ public static function getSupportedOperators()
107
+ {
108
+ return array_keys(self::$transOpStr);
109
+ }
110
+
111
+ /**
112
+ * Sets operator and version to compare with.
113
+ *
114
+ * @param string $operator
115
+ * @param string $version
116
+ *
117
+ * @throws \InvalidArgumentException if invalid operator is given.
118
+ */
119
+ public function __construct($operator, $version)
120
+ {
121
+ if (!isset(self::$transOpStr[$operator])) {
122
+ throw new \InvalidArgumentException(sprintf(
123
+ 'Invalid operator "%s" given, expected one of: %s',
124
+ $operator,
125
+ implode(', ', self::getSupportedOperators())
126
+ ));
127
+ }
128
+
129
+ $this->operator = self::$transOpStr[$operator];
130
+ $this->version = $version;
131
+ }
132
+
133
+ /**
134
+ * @param string $a
135
+ * @param string $b
136
+ * @param string $operator
137
+ * @param bool $compareBranches
138
+ *
139
+ * @throws \InvalidArgumentException if invalid operator is given.
140
+ *
141
+ * @return bool
142
+ */
143
+ public function versionCompare($a, $b, $operator, $compareBranches = false)
144
+ {
145
+ if (!isset(self::$transOpStr[$operator])) {
146
+ throw new \InvalidArgumentException(sprintf(
147
+ 'Invalid operator "%s" given, expected one of: %s',
148
+ $operator,
149
+ implode(', ', self::getSupportedOperators())
150
+ ));
151
+ }
152
+
153
+ $aIsBranch = 'dev-' === substr($a, 0, 4);
154
+ $bIsBranch = 'dev-' === substr($b, 0, 4);
155
+
156
+ if ($aIsBranch && $bIsBranch) {
157
+ return $operator === '==' && $a === $b;
158
+ }
159
+
160
+ // when branches are not comparable, we make sure dev branches never match anything
161
+ if (!$compareBranches && ($aIsBranch || $bIsBranch)) {
162
+ return false;
163
+ }
164
+
165
+ return version_compare($a, $b, $operator);
166
+ }
167
+
168
+ /**
169
+ * @param Constraint $provider
170
+ * @param bool $compareBranches
171
+ *
172
+ * @return bool
173
+ */
174
+ public function matchSpecific(Constraint $provider, $compareBranches = false)
175
+ {
176
+ $noEqualOp = str_replace('=', '', self::$transOpInt[$this->operator]);
177
+ $providerNoEqualOp = str_replace('=', '', self::$transOpInt[$provider->operator]);
178
+
179
+ $isEqualOp = self::OP_EQ === $this->operator;
180
+ $isNonEqualOp = self::OP_NE === $this->operator;
181
+ $isProviderEqualOp = self::OP_EQ === $provider->operator;
182
+ $isProviderNonEqualOp = self::OP_NE === $provider->operator;
183
+
184
+ // '!=' operator is match when other operator is not '==' operator or version is not match
185
+ // these kinds of comparisons always have a solution
186
+ if ($isNonEqualOp || $isProviderNonEqualOp) {
187
+ return !$isEqualOp && !$isProviderEqualOp
188
+ || $this->versionCompare($provider->version, $this->version, '!=', $compareBranches);
189
+ }
190
+
191
+ // an example for the condition is <= 2.0 & < 1.0
192
+ // these kinds of comparisons always have a solution
193
+ if ($this->operator !== self::OP_EQ && $noEqualOp === $providerNoEqualOp) {
194
+ return true;
195
+ }
196
+
197
+ if ($this->versionCompare($provider->version, $this->version, self::$transOpInt[$this->operator], $compareBranches)) {
198
+ // special case, e.g. require >= 1.0 and provide < 1.0
199
+ // 1.0 >= 1.0 but 1.0 is outside of the provided interval
200
+ if ($provider->version === $this->version
201
+ && self::$transOpInt[$provider->operator] === $providerNoEqualOp
202
+ && self::$transOpInt[$this->operator] !== $noEqualOp) {
203
+ return false;
204
+ }
205
+
206
+ return true;
207
+ }
208
+
209
+ return false;
210
+ }
211
+
212
+ /**
213
+ * @return string
214
+ */
215
+ public function __toString()
216
+ {
217
+ return self::$transOpInt[$this->operator] . ' ' . $this->version;
218
+ }
219
+ }
vendor/composer/semver/src/Constraint/ConstraintInterface.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of composer/semver.
5
+ *
6
+ * (c) Composer <https://github.com/composer>
7
+ *
8
+ * For the full copyright and license information, please view
9
+ * the LICENSE file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Composer\Semver\Constraint;
13
+
14
+ interface ConstraintInterface
15
+ {
16
+ /**
17
+ * @param ConstraintInterface $provider
18
+ *
19
+ * @return bool
20
+ */
21
+ public function matches(ConstraintInterface $provider);
22
+
23
+ /**
24
+ * @return string
25
+ */
26
+ public function getPrettyString();
27
+
28
+ /**
29
+ * @return string
30
+ */
31
+ public function __toString();
32
+ }
vendor/composer/semver/src/Constraint/EmptyConstraint.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of composer/semver.
5
+ *
6
+ * (c) Composer <https://github.com/composer>
7
+ *
8
+ * For the full copyright and license information, please view
9
+ * the LICENSE file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Composer\Semver\Constraint;
13
+
14
+ /**
15
+ * Defines the absence of a constraint.
16
+ */
17
+ class EmptyConstraint implements ConstraintInterface
18
+ {
19
+ /** @var string */
20
+ protected $prettyString;
21
+
22
+ /**
23
+ * @param ConstraintInterface $provider
24
+ *
25
+ * @return bool
26
+ */
27
+ public function matches(ConstraintInterface $provider)
28
+ {
29
+ return true;
30
+ }
31
+
32
+ /**
33
+ * @param $prettyString
34
+ */
35
+ public function setPrettyString($prettyString)
36
+ {
37
+ $this->prettyString = $prettyString;
38
+ }
39
+
40
+ /**
41
+ * @return string
42
+ */
43
+ public function getPrettyString()
44
+ {
45
+ if ($this->prettyString) {
46
+ return $this->prettyString;
47
+ }
48
+
49
+ return $this->__toString();
50
+ }
51
+
52
+ /**
53
+ * @return string
54
+ */
55
+ public function __toString()
56
+ {
57
+ return '[]';
58
+ }
59
+ }
vendor/composer/semver/src/Constraint/MultiConstraint.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of composer/semver.
5
+ *
6
+ * (c) Composer <https://github.com/composer>
7
+ *
8
+ * For the full copyright and license information, please view
9
+ * the LICENSE file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Composer\Semver\Constraint;
13
+
14
+ /**
15
+ * Defines a conjunctive or disjunctive set of constraints.
16
+ */
17
+ class MultiConstraint implements ConstraintInterface
18
+ {
19
+ /** @var ConstraintInterface[] */
20
+ protected $constraints;
21
+
22
+ /** @var string */
23
+ protected $prettyString;
24
+
25
+ /** @var bool */
26
+ protected $conjunctive;
27
+
28
+ /**
29
+ * @param ConstraintInterface[] $constraints A set of constraints
30
+ * @param bool $conjunctive Whether the constraints should be treated as conjunctive or disjunctive
31
+ */
32
+ public function __construct(array $constraints, $conjunctive = true)
33
+ {
34
+ $this->constraints = $constraints;
35
+ $this->conjunctive = $conjunctive;
36
+ }
37
+
38
+ /**
39
+ * @return ConstraintInterface[]
40
+ */
41
+ public function getConstraints()
42
+ {
43
+ return $this->constraints;
44
+ }
45
+
46
+ /**
47
+ * @return bool
48
+ */
49
+ public function isConjunctive()
50
+ {
51
+ return $this->conjunctive;
52
+ }
53
+
54
+ /**
55
+ * @return bool
56
+ */
57
+ public function isDisjunctive()
58
+ {
59
+ return !$this->conjunctive;
60
+ }
61
+
62
+ /**
63
+ * @param ConstraintInterface $provider
64
+ *
65
+ * @return bool
66
+ */
67
+ public function matches(ConstraintInterface $provider)
68
+ {
69
+ if (false === $this->conjunctive) {
70
+ foreach ($this->constraints as $constraint) {
71
+ if ($constraint->matches($provider)) {
72
+ return true;
73
+ }
74
+ }
75
+
76
+ return false;
77
+ }
78
+
79
+ foreach ($this->constraints as $constraint) {
80
+ if (!$constraint->matches($provider)) {
81
+ return false;
82
+ }
83
+ }
84
+
85
+ return true;
86
+ }
87
+
88
+ /**
89
+ * @param string $prettyString
90
+ */
91
+ public function setPrettyString($prettyString)
92
+ {
93
+ $this->prettyString = $prettyString;
94
+ }
95
+
96
+ /**
97
+ * @return string
98
+ */
99
+ public function getPrettyString()
100
+ {
101
+ if ($this->prettyString) {
102
+ return $this->prettyString;
103
+ }
104
+
105
+ return $this->__toString();
106
+ }
107
+
108
+ /**
109
+ * @return string
110
+ */
111
+ public function __toString()
112
+ {
113
+ $constraints = array();
114
+ foreach ($this->constraints as $constraint) {
115
+ $constraints[] = (string) $constraint;
116
+ }
117
+
118
+ return '[' . implode($this->conjunctive ? ' ' : ' || ', $constraints) . ']';
119
+ }
120
+ }
vendor/composer/semver/src/Semver.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of composer/semver.
5
+ *
6
+ * (c) Composer <https://github.com/composer>
7
+ *
8
+ * For the full copyright and license information, please view
9
+ * the LICENSE file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Composer\Semver;
13
+
14
+ use Composer\Semver\Constraint\Constraint;
15
+
16
+ class Semver
17
+ {
18
+ const SORT_ASC = 1;
19
+ const SORT_DESC = -1;
20
+
21
+ /** @var VersionParser */
22
+ private static $versionParser;
23
+
24
+ /**
25
+ * Determine if given version satisfies given constraints.
26
+ *
27
+ * @param string $version
28
+ * @param string $constraints
29
+ *
30
+ * @return bool
31
+ */
32
+ public static function satisfies($version, $constraints)
33
+ {
34
+ if (null === self::$versionParser) {
35
+ self::$versionParser = new VersionParser();
36
+ }
37
+
38
+ $versionParser = self::$versionParser;
39
+ $provider = new Constraint('==', $versionParser->normalize($version));
40
+ $constraints = $versionParser->parseConstraints($constraints);
41
+
42
+ return $constraints->matches($provider);
43
+ }
44
+
45
+ /**
46
+ * Return all versions that satisfy given constraints.
47
+ *
48
+ * @param array $versions
49
+ * @param string $constraints
50
+ *
51
+ * @return array
52
+ */
53
+ public static function satisfiedBy(array $versions, $constraints)
54
+ {
55
+ $versions = array_filter($versions, function ($version) use ($constraints) {
56
+ return Semver::satisfies($version, $constraints);
57
+ });
58
+
59
+ return array_values($versions);
60
+ }
61
+
62
+ /**
63
+ * Sort given array of versions.
64
+ *
65
+ * @param array $versions
66
+ *
67
+ * @return array
68
+ */
69
+ public static function sort(array $versions)
70
+ {
71
+ return self::usort($versions, self::SORT_ASC);
72
+ }
73
+
74
+ /**
75
+ * Sort given array of versions in reverse.
76
+ *
77
+ * @param array $versions
78
+ *
79
+ * @return array
80
+ */
81
+ public static function rsort(array $versions)
82
+ {
83
+ return self::usort($versions, self::SORT_DESC);
84
+ }
85
+
86
+ /**
87
+ * @param array $versions
88
+ * @param int $direction
89
+ *
90
+ * @return array
91
+ */
92
+ private static function usort(array $versions, $direction)
93
+ {
94
+ if (null === self::$versionParser) {
95
+ self::$versionParser = new VersionParser();
96
+ }
97
+
98
+ $versionParser = self::$versionParser;
99
+ $normalized = array();
100
+
101
+ // Normalize outside of usort() scope for minor performance increase.
102
+ // Creates an array of arrays: [[normalized, key], ...]
103
+ foreach ($versions as $key => $version) {
104
+ $normalized[] = array($versionParser->normalize($version), $key);
105
+ }
106
+
107
+ usort($normalized, function (array $left, array $right) use ($direction) {
108
+ if ($left[0] === $right[0]) {
109
+ return 0;
110
+ }
111
+
112
+ if (Comparator::lessThan($left[0], $right[0])) {
113
+ return -$direction;
114
+ }
115
+
116
+ return $direction;
117
+ });
118
+
119
+ // Recreate input array, using the original indexes which are now in sorted order.
120
+ $sorted = array();
121
+ foreach ($normalized as $item) {
122
+ $sorted[] = $versions[$item[1]];
123
+ }
124
+
125
+ return $sorted;
126
+ }
127
+ }
vendor/composer/semver/src/VersionParser.php ADDED
@@ -0,0 +1,548 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of composer/semver.
5
+ *
6
+ * (c) Composer <https://github.com/composer>
7
+ *
8
+ * For the full copyright and license information, please view
9
+ * the LICENSE file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Composer\Semver;
13
+
14
+ use Composer\Semver\Constraint\ConstraintInterface;
15
+ use Composer\Semver\Constraint\EmptyConstraint;
16
+ use Composer\Semver\Constraint\MultiConstraint;
17
+ use Composer\Semver\Constraint\Constraint;
18
+
19
+ /**
20
+ * Version parser.
21
+ *
22
+ * @author Jordi Boggiano <j.boggiano@seld.be>
23
+ */
24
+ class VersionParser
25
+ {
26
+ /**
27
+ * Regex to match pre-release data (sort of).
28
+ *
29
+ * Due to backwards compatibility:
30
+ * - Instead of enforcing hyphen, an underscore, dot or nothing at all are also accepted.
31
+ * - Only stabilities as recognized by Composer are allowed to precede a numerical identifier.
32
+ * - Numerical-only pre-release identifiers are not supported, see tests.
33
+ *
34
+ * |--------------|
35
+ * [major].[minor].[patch] -[pre-release] +[build-metadata]
36
+ *
37
+ * @var string
38
+ */
39
+ private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)((?:[.-]?\d+)*+)?)?([.-]?dev)?';
40
+
41
+ /** @var array */
42
+ private static $stabilities = array('stable', 'RC', 'beta', 'alpha', 'dev');
43
+
44
+ /**
45
+ * Returns the stability of a version.
46
+ *
47
+ * @param string $version
48
+ *
49
+ * @return string
50
+ */
51
+ public static function parseStability($version)
52
+ {
53
+ $version = preg_replace('{#.+$}i', '', $version);
54
+
55
+ if ('dev-' === substr($version, 0, 4) || '-dev' === substr($version, -4)) {
56
+ return 'dev';
57
+ }
58
+
59
+ preg_match('{' . self::$modifierRegex . '(?:\+.*)?$}i', strtolower($version), $match);
60
+ if (!empty($match[3])) {
61
+ return 'dev';
62
+ }
63
+
64
+ if (!empty($match[1])) {
65
+ if ('beta' === $match[1] || 'b' === $match[1]) {
66
+ return 'beta';
67
+ }
68
+ if ('alpha' === $match[1] || 'a' === $match[1]) {
69
+ return 'alpha';
70
+ }
71
+ if ('rc' === $match[1]) {
72
+ return 'RC';
73
+ }
74
+ }
75
+
76
+ return 'stable';
77
+ }
78
+
79
+ /**
80
+ * @param string $stability
81
+ *
82
+ * @return string
83
+ */
84
+ public static function normalizeStability($stability)
85
+ {
86
+ $stability = strtolower($stability);
87
+
88
+ return $stability === 'rc' ? 'RC' : $stability;
89
+ }
90
+
91
+ /**
92
+ * Normalizes a version string to be able to perform comparisons on it.
93
+ *
94
+ * @param string $version
95
+ * @param string $fullVersion optional complete version string to give more context
96
+ *
97
+ * @throws \UnexpectedValueException
98
+ *
99
+ * @return string
100
+ */
101
+ public function normalize($version, $fullVersion = null)
102
+ {
103
+ $version = trim($version);
104
+ if (null === $fullVersion) {
105
+ $fullVersion = $version;
106
+ }
107
+
108
+ // strip off aliasing
109
+ if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $version, $match)) {
110
+ $version = $match[1];
111
+ }
112
+
113
+ // match master-like branches
114
+ if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) {
115
+ return '9999999-dev';
116
+ }
117
+
118
+ // if requirement is branch-like, use full name
119
+ if ('dev-' === strtolower(substr($version, 0, 4))) {
120
+ return 'dev-' . substr($version, 4);
121
+ }
122
+
123
+ // strip off build metadata
124
+ if (preg_match('{^([^,\s+]++)\+[^\s]++$}', $version, $match)) {
125
+ $version = $match[1];
126
+ }
127
+
128
+ // match classical versioning
129
+ if (preg_match('{^v?(\d{1,5})(\.\d++)?(\.\d++)?(\.\d++)?' . self::$modifierRegex . '$}i', $version, $matches)) {
130
+ $version = $matches[1]
131
+ . (!empty($matches[2]) ? $matches[2] : '.0')
132
+ . (!empty($matches[3]) ? $matches[3] : '.0')
133
+ . (!empty($matches[4]) ? $matches[4] : '.0');
134
+ $index = 5;
135
+ // match date(time) based versioning
136
+ } elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)' . self::$modifierRegex . '$}i', $version, $matches)) {
137
+ $version = preg_replace('{\D}', '.', $matches[1]);
138
+ $index = 2;
139
+ }
140
+
141
+ // add version modifiers if a version was matched
142
+ if (isset($index)) {
143
+ if (!empty($matches[$index])) {
144
+ if ('stable' === $matches[$index]) {
145
+ return $version;
146
+ }
147
+ $version .= '-' . $this->expandStability($matches[$index]) . (!empty($matches[$index + 1]) ? ltrim($matches[$index + 1], '.-') : '');
148
+ }
149
+
150
+ if (!empty($matches[$index + 2])) {
151
+ $version .= '-dev';
152
+ }
153
+
154
+ return $version;
155
+ }
156
+
157
+ // match dev branches
158
+ if (preg_match('{(.*?)[.-]?dev$}i', $version, $match)) {
159
+ try {
160
+ return $this->normalizeBranch($match[1]);
161
+ } catch (\Exception $e) {
162
+ }
163
+ }
164
+
165
+ $extraMessage = '';
166
+ if (preg_match('{ +as +' . preg_quote($version) . '$}', $fullVersion)) {
167
+ $extraMessage = ' in "' . $fullVersion . '", the alias must be an exact version';
168
+ } elseif (preg_match('{^' . preg_quote($version) . ' +as +}', $fullVersion)) {
169
+ $extraMessage = ' in "' . $fullVersion . '", the alias source must be an exact version, if it is a branch name you should prefix it with dev-';
170
+ }
171
+
172
+ throw new \UnexpectedValueException('Invalid version string "' . $version . '"' . $extraMessage);
173
+ }
174
+
175
+ /**
176
+ * Extract numeric prefix from alias, if it is in numeric format, suitable for version comparison.
177
+ *
178
+ * @param string $branch Branch name (e.g. 2.1.x-dev)
179
+ *
180
+ * @return string|false Numeric prefix if present (e.g. 2.1.) or false
181
+ */
182
+ public function parseNumericAliasPrefix($branch)
183
+ {
184
+ if (preg_match('{^(?P<version>(\d++\\.)*\d++)(?:\.x)?-dev$}i', $branch, $matches)) {
185
+ return $matches['version'] . '.';
186
+ }
187
+
188
+ return false;
189
+ }
190
+
191
+ /**
192
+ * Normalizes a branch name to be able to perform comparisons on it.
193
+ *
194
+ * @param string $name
195
+ *
196
+ * @return string
197
+ */
198
+ public function normalizeBranch($name)
199
+ {
200
+ $name = trim($name);
201
+
202
+ if (in_array($name, array('master', 'trunk', 'default'))) {
203
+ return $this->normalize($name);
204
+ }
205
+
206
+ if (preg_match('{^v?(\d++)(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?$}i', $name, $matches)) {
207
+ $version = '';
208
+ for ($i = 1; $i < 5; ++$i) {
209
+ $version .= isset($matches[$i]) ? str_replace(array('*', 'X'), 'x', $matches[$i]) : '.x';
210
+ }
211
+
212
+ return str_replace('x', '9999999', $version) . '-dev';
213
+ }
214
+
215
+ return 'dev-' . $name;
216
+ }
217
+
218
+ /**
219
+ * Parses a constraint string into MultiConstraint and/or Constraint objects.
220
+ *
221
+ * @param string $constraints
222
+ *
223
+ * @return ConstraintInterface
224
+ */
225
+ public function parseConstraints($constraints)
226
+ {
227
+ $prettyConstraint = $constraints;
228
+
229
+ if (preg_match('{^([^,\s]*?)@(' . implode('|', self::$stabilities) . ')$}i', $constraints, $match)) {
230
+ $constraints = empty($match[1]) ? '*' : $match[1];
231
+ }
232
+
233
+ if (preg_match('{^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$}i', $constraints, $match)) {
234
+ $constraints = $match[1];
235
+ }
236
+
237
+ $orConstraints = preg_split('{\s*\|\|?\s*}', trim($constraints));
238
+ $orGroups = array();
239
+ foreach ($orConstraints as $constraints) {
240
+ $andConstraints = preg_split('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', $constraints);
241
+ if (count($andConstraints) > 1) {
242
+ $constraintObjects = array();
243
+ foreach ($andConstraints as $constraint) {
244
+ foreach ($this->parseConstraint($constraint) as $parsedConstraint) {
245
+ $constraintObjects[] = $parsedConstraint;
246
+ }
247
+ }
248
+ } else {
249
+ $constraintObjects = $this->parseConstraint($andConstraints[0]);
250
+ }
251
+
252
+ if (1 === count($constraintObjects)) {
253
+ $constraint = $constraintObjects[0];
254
+ } else {
255
+ $constraint = new MultiConstraint($constraintObjects);
256
+ }
257
+
258
+ $orGroups[] = $constraint;
259
+ }
260
+
261
+ if (1 === count($orGroups)) {
262
+ $constraint = $orGroups[0];
263
+ } elseif (2 === count($orGroups)
264
+ // parse the two OR groups and if they are contiguous we collapse
265
+ // them into one constraint
266
+ && $orGroups[0] instanceof MultiConstraint
267
+ && $orGroups[1] instanceof MultiConstraint
268
+ && 2 === count($orGroups[0]->getConstraints())
269
+ && 2 === count($orGroups[1]->getConstraints())
270
+ && ($a = (string) $orGroups[0])
271
+ && substr($a, 0, 3) === '[>=' && (false !== ($posA = strpos($a, '<', 4)))
272
+ && ($b = (string) $orGroups[1])
273
+ && substr($b, 0, 3) === '[>=' && (false !== ($posB = strpos($b, '<', 4)))
274
+ && substr($a, $posA + 2, -1) === substr($b, 4, $posB - 5)
275
+ ) {
276
+ $constraint = new MultiConstraint(array(
277
+ new Constraint('>=', substr($a, 4, $posA - 5)),
278
+ new Constraint('<', substr($b, $posB + 2, -1)),
279
+ ));
280
+ } else {
281
+ $constraint = new MultiConstraint($orGroups, false);
282
+ }
283
+
284
+ $constraint->setPrettyString($prettyConstraint);
285
+
286
+ return $constraint;
287
+ }
288
+
289
+ /**
290
+ * @param string $constraint
291
+ *
292
+ * @throws \UnexpectedValueException
293
+ *
294
+ * @return array
295
+ */
296
+ private function parseConstraint($constraint)
297
+ {
298
+ if (preg_match('{^([^,\s]+?)@(' . implode('|', self::$stabilities) . ')$}i', $constraint, $match)) {
299
+ $constraint = $match[1];
300
+ if ($match[2] !== 'stable') {
301
+ $stabilityModifier = $match[2];
302
+ }
303
+ }
304
+
305
+ if (preg_match('{^v?[xX*](\.[xX*])*$}i', $constraint)) {
306
+ return array(new EmptyConstraint());
307
+ }
308
+
309
+ $versionRegex = 'v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.(\d++))?' . self::$modifierRegex . '(?:\+[^\s]+)?';
310
+
311
+ // Tilde Range
312
+ //
313
+ // Like wildcard constraints, unsuffixed tilde constraints say that they must be greater than the previous
314
+ // version, to ensure that unstable instances of the current version are allowed. However, if a stability
315
+ // suffix is added to the constraint, then a >= match on the current version is used instead.
316
+ if (preg_match('{^~>?' . $versionRegex . '$}i', $constraint, $matches)) {
317
+ if (substr($constraint, 0, 2) === '~>') {
318
+ throw new \UnexpectedValueException(
319
+ 'Could not parse version constraint ' . $constraint . ': ' .
320
+ 'Invalid operator "~>", you probably meant to use the "~" operator'
321
+ );
322
+ }
323
+
324
+ // Work out which position in the version we are operating at
325
+ if (isset($matches[4]) && '' !== $matches[4]) {
326
+ $position = 4;
327
+ } elseif (isset($matches[3]) && '' !== $matches[3]) {
328
+ $position = 3;
329
+ } elseif (isset($matches[2]) && '' !== $matches[2]) {
330
+ $position = 2;
331
+ } else {
332
+ $position = 1;
333
+ }
334
+
335
+ // Calculate the stability suffix
336
+ $stabilitySuffix = '';
337
+ if (!empty($matches[5])) {
338
+ $stabilitySuffix .= '-' . $this->expandStability($matches[5]) . (!empty($matches[6]) ? $matches[6] : '');
339
+ }
340
+
341
+ if (!empty($matches[7])) {
342
+ $stabilitySuffix .= '-dev';
343
+ }
344
+
345
+ if (!$stabilitySuffix) {
346
+ $stabilitySuffix = '-dev';
347
+ }
348
+
349
+ $lowVersion = $this->manipulateVersionString($matches, $position, 0) . $stabilitySuffix;
350
+ $lowerBound = new Constraint('>=', $lowVersion);
351
+
352
+ // For upper bound, we increment the position of one more significance,
353
+ // but highPosition = 0 would be illegal
354
+ $highPosition = max(1, $position - 1);
355
+ $highVersion = $this->manipulateVersionString($matches, $highPosition, 1) . '-dev';
356
+ $upperBound = new Constraint('<', $highVersion);
357
+
358
+ return array(
359
+ $lowerBound,
360
+ $upperBound,
361
+ );
362
+ }
363
+
364
+ // Caret Range
365
+ //
366
+ // Allows changes that do not modify the left-most non-zero digit in the [major, minor, patch] tuple.
367
+ // In other words, this allows patch and minor updates for versions 1.0.0 and above, patch updates for
368
+ // versions 0.X >=0.1.0, and no updates for versions 0.0.X
369
+ if (preg_match('{^\^' . $versionRegex . '($)}i', $constraint, $matches)) {
370
+ // Work out which position in the version we are operating at
371
+ if ('0' !== $matches[1] || '' === $matches[2]) {
372
+ $position = 1;
373
+ } elseif ('0' !== $matches[2] || '' === $matches[3]) {
374
+ $position = 2;
375
+ } else {
376
+ $position = 3;
377
+ }
378
+
379
+ // Calculate the stability suffix
380
+ $stabilitySuffix = '';
381
+ if (empty($matches[5]) && empty($matches[7])) {
382
+ $stabilitySuffix .= '-dev';
383
+ }
384
+
385
+ $lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1));
386
+ $lowerBound = new Constraint('>=', $lowVersion);
387
+
388
+ // For upper bound, we increment the position of one more significance,
389
+ // but highPosition = 0 would be illegal
390
+ $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
391
+ $upperBound = new Constraint('<', $highVersion);
392
+
393
+ return array(
394
+ $lowerBound,
395
+ $upperBound,
396
+ );
397
+ }
398
+
399
+ // X Range
400
+ //
401
+ // Any of X, x, or * may be used to "stand in" for one of the numeric values in the [major, minor, patch] tuple.
402
+ // A partial version range is treated as an X-Range, so the special character is in fact optional.
403
+ if (preg_match('{^v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.[xX*])++$}', $constraint, $matches)) {
404
+ if (isset($matches[3]) && '' !== $matches[3]) {
405
+ $position = 3;
406
+ } elseif (isset($matches[2]) && '' !== $matches[2]) {
407
+ $position = 2;
408
+ } else {
409
+ $position = 1;
410
+ }
411
+
412
+ $lowVersion = $this->manipulateVersionString($matches, $position) . '-dev';
413
+ $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
414
+
415
+ if ($lowVersion === '0.0.0.0-dev') {
416
+ return array(new Constraint('<', $highVersion));
417
+ }
418
+
419
+ return array(
420
+ new Constraint('>=', $lowVersion),
421
+ new Constraint('<', $highVersion),
422
+ );
423
+ }
424
+
425
+ // Hyphen Range
426
+ //
427
+ // Specifies an inclusive set. If a partial version is provided as the first version in the inclusive range,
428
+ // then the missing pieces are replaced with zeroes. If a partial version is provided as the second version in
429
+ // the inclusive range, then all versions that start with the supplied parts of the tuple are accepted, but
430
+ // nothing that would be greater than the provided tuple parts.
431
+ if (preg_match('{^(?P<from>' . $versionRegex . ') +- +(?P<to>' . $versionRegex . ')($)}i', $constraint, $matches)) {
432
+ // Calculate the stability suffix
433
+ $lowStabilitySuffix = '';
434
+ if (empty($matches[6]) && empty($matches[8])) {
435
+ $lowStabilitySuffix = '-dev';
436
+ }
437
+
438
+ $lowVersion = $this->normalize($matches['from']);
439
+ $lowerBound = new Constraint('>=', $lowVersion . $lowStabilitySuffix);
440
+
441
+ $empty = function ($x) {
442
+ return ($x === 0 || $x === '0') ? false : empty($x);
443
+ };
444
+
445
+ if ((!$empty($matches[11]) && !$empty($matches[12])) || !empty($matches[14]) || !empty($matches[16])) {
446
+ $highVersion = $this->normalize($matches['to']);
447
+ $upperBound = new Constraint('<=', $highVersion);
448
+ } else {
449
+ $highMatch = array('', $matches[10], $matches[11], $matches[12], $matches[13]);
450
+ $highVersion = $this->manipulateVersionString($highMatch, $empty($matches[11]) ? 1 : 2, 1) . '-dev';
451
+ $upperBound = new Constraint('<', $highVersion);
452
+ }
453
+
454
+ return array(
455
+ $lowerBound,
456
+ $upperBound,
457
+ );
458
+ }
459
+
460
+ // Basic Comparators
461
+ if (preg_match('{^(<>|!=|>=?|<=?|==?)?\s*(.*)}', $constraint, $matches)) {
462
+ try {
463
+ $version = $this->normalize($matches[2]);
464
+
465
+ if (!empty($stabilityModifier) && $this->parseStability($version) === 'stable') {
466
+ $version .= '-' . $stabilityModifier;
467
+ } elseif ('<' === $matches[1] || '>=' === $matches[1]) {
468
+ if (!preg_match('/-' . self::$modifierRegex . '$/', strtolower($matches[2]))) {
469
+ if (substr($matches[2], 0, 4) !== 'dev-') {
470
+ $version .= '-dev';
471
+ }
472
+ }
473
+ }
474
+
475
+ return array(new Constraint($matches[1] ?: '=', $version));
476
+ } catch (\Exception $e) {
477
+ }
478
+ }
479
+
480
+ $message = 'Could not parse version constraint ' . $constraint;
481
+ if (isset($e)) {
482
+ $message .= ': ' . $e->getMessage();
483
+ }
484
+
485
+ throw new \UnexpectedValueException($message);
486
+ }
487
+
488
+ /**
489
+ * Increment, decrement, or simply pad a version number.
490
+ *
491
+ * Support function for {@link parseConstraint()}
492
+ *
493
+ * @param array $matches Array with version parts in array indexes 1,2,3,4
494
+ * @param int $position 1,2,3,4 - which segment of the version to increment/decrement
495
+ * @param int $increment
496
+ * @param string $pad The string to pad version parts after $position
497
+ *
498
+ * @return string The new version
499
+ */
500
+ private function manipulateVersionString($matches, $position, $increment = 0, $pad = '0')
501
+ {
502
+ for ($i = 4; $i > 0; --$i) {
503
+ if ($i > $position) {
504
+ $matches[$i] = $pad;
505
+ } elseif ($i === $position && $increment) {
506
+ $matches[$i] += $increment;
507
+ // If $matches[$i] was 0, carry the decrement
508
+ if ($matches[$i] < 0) {
509
+ $matches[$i] = $pad;
510
+ --$position;
511
+
512
+ // Return null on a carry overflow
513
+ if ($i === 1) {
514
+ return;
515
+ }
516
+ }
517
+ }
518
+ }
519
+
520
+ return $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.' . $matches[4];
521
+ }
522
+
523
+ /**
524
+ * Expand shorthand stability string to long version.
525
+ *
526
+ * @param string $stability
527
+ *
528
+ * @return string
529
+ */
530
+ private function expandStability($stability)
531
+ {
532
+ $stability = strtolower($stability);
533
+
534
+ switch ($stability) {
535
+ case 'a':
536
+ return 'alpha';
537
+ case 'b':
538
+ return 'beta';
539
+ case 'p':
540
+ case 'pl':
541
+ return 'patch';
542
+ case 'rc':
543
+ return 'RC';
544
+ default:
545
+ return $stability;
546
+ }
547
+ }
548
+ }
vendor/composer/xdebug-handler/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Composer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
vendor/composer/xdebug-handler/composer.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "composer/xdebug-handler",
3
+ "description": "Restarts a process without xdebug.",
4
+ "type": "library",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "xdebug",
8
+ "performance"
9
+ ],
10
+ "authors": [
11
+ {
12
+ "name": "John Stevenson",
13
+ "email": "john-stevenson@blueyonder.co.uk"
14
+ }
15
+ ],
16
+ "support": {
17
+ "irc": "irc://irc.freenode.org/composer",
18
+ "issues": "https://github.com/composer/xdebug-handler/issues"
19
+ },
20
+ "require": {
21
+ "php": "^5.3.2 || ^7.0",
22
+ "psr/log": "^1.0"
23
+ },
24
+ "require-dev": {
25
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5"
26
+ },
27
+ "autoload": {
28
+ "psr-4": {
29
+ "Composer\\XdebugHandler\\": "src"
30
+ }
31
+ },
32
+ "autoload-dev": {
33
+ "psr-4": {
34
+ "Composer\\XdebugHandler\\": "tests"
35
+ }
36
+ },
37
+ "scripts": {
38
+ "test": "phpunit"
39
+ }
40
+ }
vendor/composer/xdebug-handler/src/PhpConfig.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of composer/xdebug-handler.
5
+ *
6
+ * (c) Composer <https://github.com/composer>
7
+ *
8
+ * For the full copyright and license information, please view
9
+ * the LICENSE file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Composer\XdebugHandler;
13
+
14
+ /**
15
+ * @author John Stevenson <john-stevenson@blueyonder.co.uk>
16
+ */
17
+ class PhpConfig
18
+ {
19
+ /**
20
+ * Use the original PHP configuration
21
+ *
22
+ * @return array PHP cli options
23
+ */
24
+ public function useOriginal()
25
+ {
26
+ $this->getDataAndReset();
27
+ return array();
28
+ }
29
+
30
+ /**
31
+ * Use standard restart settings
32
+ *
33
+ * @return array PHP cli options
34
+ */
35
+ public function useStandard()
36
+ {
37
+ if ($data = $this->getDataAndReset()) {
38
+ return array('-n', '-c', $data['tmpIni']);
39
+ }
40
+
41
+ return array();
42
+ }
43
+
44
+ /**
45
+ * Use environment variables to persist settings
46
+ *
47
+ * @return array PHP cli options
48
+ */
49
+ public function usePersistent()
50
+ {
51
+ if ($data = $this->getDataAndReset()) {
52
+ Process::setEnv('PHPRC', $data['tmpIni']);
53
+ Process::setEnv('PHP_INI_SCAN_DIR', '');
54
+ }
55
+
56
+ return array();
57
+ }
58
+
59
+ /**
60
+ * Returns restart data if available and resets the environment
61
+ *
62
+ * @return array|null
63
+ */
64
+ private function getDataAndReset()
65
+ {
66
+ if ($data = XdebugHandler::getRestartSettings()) {
67
+ Process::setEnv('PHPRC', $data['phprc']);
68
+ Process::setEnv('PHP_INI_SCAN_DIR', $data['scanDir']);
69
+ }
70
+
71
+ return $data;
72
+ }
73
+ }
vendor/composer/xdebug-handler/src/Process.php ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of composer/xdebug-handler.
5
+ *
6
+ * (c) Composer <https://github.com/composer>
7
+ *
8
+ * For the full copyright and license information, please view
9
+ * the LICENSE file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Composer\XdebugHandler;
13
+
14
+ /**
15
+ * Provides utility functions to prepare a child process command-line and set
16
+ * environment variables in that process.
17
+ *
18
+ * @author John Stevenson <john-stevenson@blueyonder.co.uk>
19
+ * @internal
20
+ */
21
+ class Process
22
+ {
23
+ /**
24
+ * Returns an array of parameters, including a color option if required
25
+ *
26
+ * A color option is needed because child process output is piped.
27
+ *
28
+ * @param array $args The script parameters
29
+ * @param string $colorOption The long option to force color output
30
+ *
31
+ * @return array
32
+ */
33
+ public static function addColorOption(array $args, $colorOption)
34
+ {
35
+ if (!$colorOption
36
+ || in_array($colorOption, $args)
37
+ || !preg_match('/^--([a-z]+$)|(^--[a-z]+=)/', $colorOption, $matches)) {
38
+ return $args;
39
+ }
40
+
41
+ if (isset($matches[2])) {
42
+ // Handle --color(s)= options
43
+ if (false !== ($index = array_search($matches[2].'auto', $args))) {
44
+ $args[$index] = $colorOption;
45
+ return $args;
46
+ } elseif (preg_grep('/^'.$matches[2].'/', $args)) {
47
+ return $args;
48
+ }
49
+ } elseif (in_array('--no-'.$matches[1], $args)) {
50
+ return $args;
51
+ }
52
+
53
+ if (false !== ($index = array_search('--', $args))) {
54
+ // Position option before double-dash delimiter
55
+ array_splice($args, $index, 0, $colorOption);
56
+ } else {
57
+ $args[] = $colorOption;
58
+ }
59
+
60
+ return $args;
61
+ }
62
+
63
+ /**
64
+ * Escapes a string to be used as a shell argument.
65
+ *
66
+ * From https://github.com/johnstevenson/winbox-args
67
+ * MIT Licensed (c) John Stevenson <john-stevenson@blueyonder.co.uk>
68
+ *
69
+ * @param string $arg The argument to be escaped
70
+ * @param bool $meta Additionally escape cmd.exe meta characters
71
+ * @param bool $module The argument is the module to invoke
72
+ *
73
+ * @return string The escaped argument
74
+ */
75
+ public static function escape($arg, $meta = true, $module = false)
76
+ {
77
+ if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
78
+ return "'".str_replace("'", "'\\''", $arg)."'";
79
+ }
80
+
81
+ $quote = strpbrk($arg, " \t") !== false || $arg === '';
82
+
83
+ $arg = preg_replace('/(\\\\*)"/', '$1$1\\"', $arg, -1, $dquotes);
84
+
85
+ if ($meta) {
86
+ $meta = $dquotes || preg_match('/%[^%]+%/', $arg);
87
+
88
+ if (!$meta) {
89
+ $quote = $quote || strpbrk($arg, '^&|<>()') !== false;
90
+ } elseif ($module && !$dquotes && $quote) {
91
+ $meta = false;
92
+ }
93
+ }
94
+
95
+ if ($quote) {
96
+ $arg = '"'.preg_replace('/(\\\\*)$/', '$1$1', $arg).'"';
97
+ }
98
+
99
+ if ($meta) {
100
+ $arg = preg_replace('/(["^&|<>()%])/', '^$1', $arg);
101
+ }
102
+
103
+ return $arg;
104
+ }
105
+
106
+ /**
107
+ * Returns true if the output stream supports colors
108
+ *
109
+ * This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo
110
+ * terminals via named pipes, so we can only check the environment.
111
+ *
112
+ * @param mixed $output A valid CLI output stream
113
+ *
114
+ * @return bool
115
+ */
116
+ public static function supportsColor($output)
117
+ {
118
+ if (defined('PHP_WINDOWS_VERSION_BUILD')) {
119
+ return (function_exists('sapi_windows_vt100_support')
120
+ && sapi_windows_vt100_support($output))
121
+ || false !== getenv('ANSICON')
122
+ || 'ON' === getenv('ConEmuANSI')
123
+ || 'xterm' === getenv('TERM');
124
+ }
125
+
126
+ if (function_exists('stream_isatty')) {
127
+ return stream_isatty($output);
128
+ } elseif (function_exists('posix_isatty')) {
129
+ return posix_isatty($output);
130
+ }
131
+
132
+ $stat = fstat($output);
133
+ // Check if formatted mode is S_IFCHR
134
+ return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
135
+ }
136
+
137
+ /**
138
+ * Makes putenv environment changes available in $_SERVER
139
+ *
140
+ * @param string $name
141
+ * @param string|false $value A false value unsets the variable
142
+ *
143
+ * @return bool Whether the environment variable was set
144
+ */
145
+ public static function setEnv($name, $value = false)
146
+ {
147
+ $unset = false === $value;
148
+
149
+ if (!putenv($unset ? $name : $name.'='.$value)) {
150
+ return false;
151
+ }
152
+
153
+ if ($unset) {
154
+ unset($_SERVER[$name]);
155
+ } else {
156
+ $_SERVER[$name] = $value;
157
+ }
158
+ return true;
159
+ }
160
+ }
vendor/composer/xdebug-handler/src/Status.php ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of composer/xdebug-handler.
5
+ *
6
+ * (c) Composer <https://github.com/composer>
7
+ *
8
+ * For the full copyright and license information, please view
9
+ * the LICENSE file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Composer\XdebugHandler;
13
+
14
+ use Psr\Log\LoggerInterface;
15
+ use Psr\Log\LogLevel;
16
+
17
+ /**
18
+ * @author John Stevenson <john-stevenson@blueyonder.co.uk>
19
+ * @internal
20
+ */
21
+ class Status
22
+ {
23
+ const ENV_RESTART = 'XDEBUG_HANDLER_RESTART';
24
+ const CHECK = 'Check';
25
+ const ERROR = 'Error';
26
+ const INFO = 'Info';
27
+ const NORESTART = 'NoRestart';
28
+ const RESTART = 'Restart';
29
+ const RESTARTING = 'Restarting';
30
+ const RESTARTED = 'Restarted';
31
+
32
+ private $debug;
33
+ private $envAllowXdebug;
34
+ private $loaded;
35
+ private $logger;
36
+ private $time;
37
+
38
+ /**
39
+ * Constructor
40
+ *
41
+ * @param string $envAllowXdebug Prefixed _ALLOW_XDEBUG name
42
+ * @param bool $debug Whether debug output is required
43
+ */
44
+ public function __construct($envAllowXdebug, $debug)
45
+ {
46
+ $start = getenv(self::ENV_RESTART);
47
+ Process::setEnv(self::ENV_RESTART);
48
+ $this->time = $start ? round((microtime(true) - $start) * 1000) : 0;
49
+
50
+ $this->envAllowXdebug = $envAllowXdebug;
51
+ $this->debug = $debug && defined('STDERR');
52
+ }
53
+
54
+ /**
55
+ * @param LoggerInterface $logger
56
+ */
57
+ public function setLogger(LoggerInterface $logger)
58
+ {
59
+ $this->logger = $logger;
60
+ }
61
+
62
+ /**
63
+ * Calls a handler method to report a message
64
+ *
65
+ * @param string $op The handler constant
66
+ * @param null|string $data Data required by the handler
67
+ */
68
+ public function report($op, $data)
69
+ {
70
+ if ($this->logger || $this->debug) {
71
+ call_user_func(array($this, 'report'.$op), $data);
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Outputs a status message
77
+ *
78
+ * @param string $text
79
+ * @param string $level
80
+ */
81
+ private function output($text, $level = null)
82
+ {
83
+ if ($this->logger) {
84
+ $this->logger->log($level ?: LogLevel::DEBUG, $text);
85
+ }
86
+
87
+ if ($this->debug) {
88
+ fwrite(STDERR, sprintf('xdebug-handler[%d] %s', getmypid(), $text.PHP_EOL));
89
+ }
90
+ }
91
+
92
+ private function reportCheck($loaded)
93
+ {
94
+ $this->loaded = $loaded;
95
+ $this->output('Checking '.$this->envAllowXdebug);
96
+ }
97
+
98
+ private function reportError($error)
99
+ {
100
+ $this->output(sprintf('No restart (%s)', $error), LogLevel::WARNING);
101
+ }
102
+
103
+ private function reportInfo($info)
104
+ {
105
+ $this->output($info);
106
+ }
107
+
108
+ private function reportNoRestart()
109
+ {
110
+ $this->output($this->getLoadedMessage());
111
+
112
+ if ($this->loaded) {
113
+ $text = sprintf('No restart (%s)', $this->getEnvAllow());
114
+ if (!getenv($this->envAllowXdebug)) {
115
+ $text .= ' Allowed by application';
116
+ }
117
+ $this->output($text);
118
+ }
119
+ }
120
+
121
+ private function reportRestart()
122
+ {
123
+ $this->output($this->getLoadedMessage());
124
+ Process::setEnv(self::ENV_RESTART, (string) microtime(true));
125
+ }
126
+
127
+ private function reportRestarted()
128
+ {
129
+ $loaded = $this->getLoadedMessage();
130
+ $text = sprintf('Restarted (%d ms). %s', $this->time, $loaded);
131
+ $level = $this->loaded ? LogLevel::WARNING : null;
132
+ $this->output($text, $level);
133
+ }
134
+
135
+ private function reportRestarting($command)
136
+ {
137
+ $text = sprintf('Process restarting (%s)', $this->getEnvAllow());
138
+ $this->output($text);
139
+ $text = 'Running '.$command;
140
+ $this->output($text);
141
+ }
142
+
143
+ /**
144
+ * Returns the _ALLOW_XDEBUG environment variable as name=value
145
+ *
146
+ * @return string
147
+ */
148
+ private function getEnvAllow()
149
+ {
150
+ return $this->envAllowXdebug.'='.getenv($this->envAllowXdebug);
151
+ }
152
+
153
+ /**
154
+ * Returns the xdebug status and version
155
+ *
156
+ * @return string
157
+ */
158
+ private function getLoadedMessage()
159
+ {
160
+ $loaded = $this->loaded ? sprintf('loaded (%s)', $this->loaded) : 'not loaded';
161
+ return 'The xdebug extension is '.$loaded;
162
+ }
163
+ }
vendor/composer/xdebug-handler/src/XdebugHandler.php ADDED
@@ -0,0 +1,540 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of composer/xdebug-handler.
5
+ *
6
+ * (c) Composer <https://github.com/composer>
7
+ *
8
+ * For the full copyright and license information, please view
9
+ * the LICENSE file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Composer\XdebugHandler;
13
+
14
+ use Psr\Log\LoggerInterface;
15
+
16
+ /**
17
+ * @author John Stevenson <john-stevenson@blueyonder.co.uk>
18
+ */
19
+ class XdebugHandler
20
+ {
21
+ const SUFFIX_ALLOW = '_ALLOW_XDEBUG';
22
+ const SUFFIX_INIS = '_ORIGINAL_INIS';
23
+ const RESTART_ID = 'internal';
24
+ const RESTART_SETTINGS = 'XDEBUG_HANDLER_SETTINGS';
25
+ const DEBUG = 'XDEBUG_HANDLER_DEBUG';
26
+
27
+ /** @var string|null */
28
+ protected $tmpIni;
29
+
30
+ private static $inRestart;
31
+ private static $name;
32
+ private static $skipped;
33
+
34
+ private $cli;
35
+ private $colorOption;
36
+ private $debug;
37
+ private $envAllowXdebug;
38
+ private $envOriginalInis;
39
+ private $loaded;
40
+ private $persistent;
41
+ private $script;
42
+ /** @var Status|null */
43
+ private $statusWriter;
44
+
45
+ /**
46
+ * Constructor
47
+ *
48
+ * The $envPrefix is used to create distinct environment variables. It is
49
+ * uppercased and prepended to the default base values. For example 'myapp'
50
+ * would result in MYAPP_ALLOW_XDEBUG and MYAPP_ORIGINAL_INIS.
51
+ *
52
+ * @param string $envPrefix Value used in environment variables
53
+ * @param string $colorOption Command-line long option to force color output
54
+ * @throws \RuntimeException If a parameter is invalid
55
+ */
56
+ public function __construct($envPrefix, $colorOption = '')
57
+ {
58
+ if (!is_string($envPrefix) || empty($envPrefix) || !is_string($colorOption)) {
59
+ throw new \RuntimeException('Invalid constructor parameter');
60
+ }
61
+
62
+ self::$name = strtoupper($envPrefix);
63
+ $this->envAllowXdebug = self::$name.self::SUFFIX_ALLOW;
64
+ $this->envOriginalInis = self::$name.self::SUFFIX_INIS;
65
+
66
+ $this->colorOption = $colorOption;
67
+
68
+ if (extension_loaded('xdebug')) {
69
+ $ext = new \ReflectionExtension('xdebug');
70
+ $this->loaded = $ext->getVersion() ?: 'unknown';
71
+ }
72
+
73
+ if ($this->cli = PHP_SAPI === 'cli') {
74
+ $this->debug = getenv(self::DEBUG);
75
+ }
76
+
77
+ $this->statusWriter = new Status($this->envAllowXdebug, (bool) $this->debug);
78
+ }
79
+
80
+ /**
81
+ * Activates status message output to a PSR3 logger
82
+ *
83
+ * @param LoggerInterface $logger
84
+ *
85
+ * @return $this
86
+ */
87
+ public function setLogger(LoggerInterface $logger)
88
+ {
89
+ $this->statusWriter->setLogger($logger);
90
+ return $this;
91
+ }
92
+
93
+ /**
94
+ * Sets the main script location if it cannot be called from argv
95
+ *
96
+ * @param string $script
97
+ *
98
+ * @return $this
99
+ */
100
+ public function setMainScript($script)
101
+ {
102
+ $this->script = $script;
103
+ return $this;
104
+ }
105
+
106
+ /**
107
+ * Persist the settings to keep xdebug out of sub-processes
108
+ *
109
+ * @return $this
110
+ */
111
+ public function setPersistent()
112
+ {
113
+ $this->persistent = true;
114
+ return $this;
115
+ }
116
+
117
+ /**
118
+ * Checks if xdebug is loaded and the process needs to be restarted
119
+ *
120
+ * This behaviour can be disabled by setting the MYAPP_ALLOW_XDEBUG
121
+ * environment variable to 1. This variable is used internally so that
122
+ * restarted process is created only once.
123
+ */
124
+ public function check()
125
+ {
126
+ $this->notify(Status::CHECK, $this->loaded);
127
+ $envArgs = explode('|', (string) getenv($this->envAllowXdebug));
128
+
129
+ if (empty($envArgs[0]) && $this->requiresRestart((bool) $this->loaded)) {
130
+ // Restart required
131
+ $this->notify(Status::RESTART);
132
+
133
+ if ($this->prepareRestart()) {
134
+ $command = $this->getCommand();
135
+ $this->notify(Status::RESTARTING, $command);
136
+ $this->restart($command);
137
+ }
138
+ return;
139
+ }
140
+
141
+ if (self::RESTART_ID === $envArgs[0] && count($envArgs) === 5) {
142
+ // Restarting, so unset environment variable and use saved values
143
+ $this->notify(Status::RESTARTED);
144
+
145
+ Process::setEnv($this->envAllowXdebug);
146
+ self::$inRestart = true;
147
+
148
+ if (!$this->loaded) {
149
+ // Skipped version is only set if xdebug is not loaded
150
+ self::$skipped = $envArgs[1];
151
+ }
152
+
153
+ // Put restart settings in the environment
154
+ $this->setEnvRestartSettings($envArgs);
155
+ return;
156
+ }
157
+
158
+ $this->notify(Status::NORESTART);
159
+
160
+ if ($settings = self::getRestartSettings()) {
161
+ // Called with existing settings, so sync our settings
162
+ $this->syncSettings($settings);
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Returns an array of php.ini locations with at least one entry
168
+ *
169
+ * The equivalent of calling php_ini_loaded_file then php_ini_scanned_files.
170
+ * The loaded ini location is the first entry and may be empty.
171
+ *
172
+ * @return array
173
+ */
174
+ public static function getAllIniFiles()
175
+ {
176
+ if (!empty(self::$name)) {
177
+ $env = getenv(self::$name.self::SUFFIX_INIS);
178
+
179
+ if (false !== $env) {
180
+ return explode(PATH_SEPARATOR, $env);
181
+ }
182
+ }
183
+
184
+ $paths = array((string) php_ini_loaded_file());
185
+
186
+ if ($scanned = php_ini_scanned_files()) {
187
+ $paths = array_merge($paths, array_map('trim', explode(',', $scanned)));
188
+ }
189
+
190
+ return $paths;
191
+ }
192
+
193
+ /**
194
+ * Returns an array of restart settings or null
195
+ *
196
+ * Settings will be available if the current process was restarted, or
197
+ * called with the settings from an existing restart.
198
+ *
199
+ * @return array|null
200
+ */
201
+ public static function getRestartSettings()
202
+ {
203
+ $envArgs = explode('|', (string) getenv(self::RESTART_SETTINGS));
204
+
205
+ if (count($envArgs) !== 6
206
+ || (!self::$inRestart && php_ini_loaded_file() !== $envArgs[0])) {
207
+ return;
208
+ }
209
+
210
+ return array(
211
+ 'tmpIni' => $envArgs[0],
212
+ 'scannedInis' => (bool) $envArgs[1],
213
+ 'scanDir' => '*' === $envArgs[2] ? false : $envArgs[2],
214
+ 'phprc' => '*' === $envArgs[3] ? false : $envArgs[3],
215
+ 'inis' => explode(PATH_SEPARATOR, $envArgs[4]),
216
+ 'skipped' => $envArgs[5],
217
+ );
218
+ }
219
+
220
+ /**
221
+ * Returns the xdebug version that triggered a successful restart
222
+ *
223
+ * @return string
224
+ */
225
+ public static function getSkippedVersion()
226
+ {
227
+ return (string) self::$skipped;
228
+ }
229
+
230
+ /**
231
+ * Returns true if xdebug is loaded, or as directed by an extending class
232
+ *
233
+ * @param bool $isLoaded Whether xdebug is loaded
234
+ *
235
+ * @return bool
236
+ */
237
+ protected function requiresRestart($isLoaded)
238
+ {
239
+ return $isLoaded;
240
+ }
241
+
242
+ /**
243
+ * Allows an extending class to access the tmpIni
244
+ *
245
+ * @param string $command
246
+ */
247
+ protected function restart($command)
248
+ {
249
+ $this->doRestart($command);
250
+ }
251
+
252
+ /**
253
+ * Executes the restarted command then deletes the tmp ini
254
+ *
255
+ * @param string $command
256
+ */
257
+ private function doRestart($command)
258
+ {
259
+ passthru($command, $exitCode);
260
+ $this->notify(Status::INFO, 'Restarted process exited '.$exitCode);
261
+
262
+ if ($this->debug === '2') {
263
+ $this->notify(Status::INFO, 'Temp ini saved: '.$this->tmpIni);
264
+ } else {
265
+ @unlink($this->tmpIni);
266
+ }
267
+
268
+ exit($exitCode);
269
+ }
270
+
271
+ /**
272
+ * Returns true if everything was written for the restart
273
+ *
274
+ * If any of the following fails (however unlikely) we must return false to
275
+ * stop potential recursion:
276
+ * - tmp ini file creation
277
+ * - environment variable creation
278
+ *
279
+ * @return bool
280
+ */
281
+ private function prepareRestart()
282
+ {
283
+ $error = '';
284
+ $iniFiles = self::getAllIniFiles();
285
+ $scannedInis = count($iniFiles) > 1;
286
+ $tmpDir = sys_get_temp_dir();
287
+
288
+ if (!$this->cli) {
289
+ $error = 'Unsupported SAPI: '.PHP_SAPI;
290
+ } elseif (!defined('PHP_BINARY')) {
291
+ $error = 'PHP version is too old: '.PHP_VERSION;
292
+ } elseif (false !== strpos(ini_get('disable_functions'), 'passthru')) {
293
+ $error = 'Required function is disabled: passthru';
294
+ } elseif (!$this->checkScanDirConfig()) {
295
+ $error = 'PHP version does not report scanned inis: '.PHP_VERSION;
296
+ } elseif (!$this->checkMainScript()) {
297
+ $error = 'Unable to access main script: '.$this->script;
298
+ } elseif (!$this->writeTmpIni($iniFiles, $tmpDir, $error)) {
299
+ $error = $error ?: 'Unable to create temp ini file at: '.$tmpDir;
300
+ } elseif (!$this->setEnvironment($scannedInis, $iniFiles)) {
301
+ $error = 'Unable to set environment variables';
302
+ }
303
+
304
+ if ($error) {
305
+ $this->notify(Status::ERROR, $error);
306
+ }
307
+
308
+ return empty($error);
309
+ }
310
+
311
+ /**
312
+ * Returns true if the tmp ini file was written
313
+ *
314
+ * @param array $iniFiles All ini files used in the current process
315
+ * @param string $tmpDir The system temporary directory
316
+ * @param string $error Set by method if ini file cannot be read
317
+ *
318
+ * @return bool
319
+ */
320
+ private function writeTmpIni(array $iniFiles, $tmpDir, &$error)
321
+ {
322
+ if (!$this->tmpIni = @tempnam($tmpDir, '')) {
323
+ return false;
324
+ }
325
+
326
+ // $iniFiles has at least one item and it may be empty
327
+ if (empty($iniFiles[0])) {
328
+ array_shift($iniFiles);
329
+ }
330
+
331
+ $content = '';
332
+ $regex = '/^\s*(zend_extension\s*=.*xdebug.*)$/mi';
333
+
334
+ foreach ($iniFiles as $file) {
335
+ // Check for inaccessible ini files
336
+ if (!$data = @file_get_contents($file)) {
337
+ $error = 'Unable to read ini: '.$file;
338
+ return false;
339
+ }
340
+ $content .= preg_replace($regex, ';$1', $data).PHP_EOL;
341
+ }
342
+
343
+ // Merge loaded settings into our ini content, if it is valid
344
+ if ($config = parse_ini_string($content)) {
345
+ $loaded = ini_get_all(null, false);
346
+ $content .= $this->mergeLoadedConfig($loaded, $config);
347
+ }
348
+
349
+ // Work-around for https://bugs.php.net/bug.php?id=75932
350
+ $content .= 'opcache.enable_cli=0'.PHP_EOL;
351
+
352
+ return @file_put_contents($this->tmpIni, $content);
353
+ }
354
+
355
+ /**
356
+ * Returns the restart command line
357
+ *
358
+ * @return string
359
+ */
360
+ private function getCommand()
361
+ {
362
+ $php = array(PHP_BINARY);
363
+ $args = array_slice($_SERVER['argv'], 1);
364
+
365
+ if (!$this->persistent) {
366
+ // Use command-line options
367
+ array_push($php, '-n', '-c', $this->tmpIni);
368
+ }
369
+
370
+ if (defined('STDOUT') && Process::supportsColor(STDOUT)) {
371
+ $args = Process::addColorOption($args, $this->colorOption);
372
+ }
373
+
374
+ $args = array_merge($php, array($this->script), $args);
375
+
376
+ $cmd = Process::escape(array_shift($args), true, true);
377
+ foreach ($args as $arg) {
378
+ $cmd .= ' '.Process::escape($arg);
379
+ }
380
+
381
+ return $cmd;
382
+ }
383
+
384
+ /**
385
+ * Returns true if the restart environment variables were set
386
+ *
387
+ * No need to update $_SERVER since this is set in the restarted process.
388
+ *
389
+ * @param bool $scannedInis Whether there were scanned ini files
390
+ * @param array $iniFiles All ini files used in the current process
391
+ *
392
+ * @return bool
393
+ */
394
+ private function setEnvironment($scannedInis, array $iniFiles)
395
+ {
396
+ $scanDir = getenv('PHP_INI_SCAN_DIR');
397
+ $phprc = getenv('PHPRC');
398
+
399
+ // Make original inis available to restarted process
400
+ if (!putenv($this->envOriginalInis.'='.implode(PATH_SEPARATOR, $iniFiles))) {
401
+ return false;
402
+ }
403
+
404
+ if ($this->persistent) {
405
+ // Use the environment to persist the settings
406
+ if (!putenv('PHP_INI_SCAN_DIR=') || !putenv('PHPRC='.$this->tmpIni)) {
407
+ return false;
408
+ }
409
+ }
410
+
411
+ // Flag restarted process and save values for it to use
412
+ $envArgs = array(
413
+ self::RESTART_ID,
414
+ $this->loaded,
415
+ (int) $scannedInis,
416
+ false === $scanDir ? '*' : $scanDir,
417
+ false === $phprc ? '*' : $phprc,
418
+ );
419
+
420
+ return putenv($this->envAllowXdebug.'='.implode('|', $envArgs));
421
+ }
422
+
423
+ /**
424
+ * Logs status messages
425
+ *
426
+ * @param string $op Status handler constant
427
+ * @param null|string $data Optional data
428
+ */
429
+ private function notify($op, $data = null)
430
+ {
431
+ $this->statusWriter->report($op, $data);
432
+ }
433
+
434
+ /**
435
+ * Returns default, changed and command-line ini settings
436
+ *
437
+ * @param array $loadedConfig All current ini settings
438
+ * @param array $iniConfig Settings from user ini files
439
+ *
440
+ * @return string
441
+ */
442
+ private function mergeLoadedConfig(array $loadedConfig, array $iniConfig)
443
+ {
444
+ $content = '';
445
+
446
+ foreach ($loadedConfig as $name => $value) {
447
+ // Value will either be null, string or array (HHVM only)
448
+ if (!is_string($value)
449
+ || strpos($name, 'xdebug') === 0
450
+ || $name === 'apc.mmap_file_mask') {
451
+ continue;
452
+ }
453
+
454
+ if (!isset($iniConfig[$name]) || $iniConfig[$name] !== $value) {
455
+ // Double-quote escape each value
456
+ $content .= $name.'="'.addcslashes($value, '\\"').'"'.PHP_EOL;
457
+ }
458
+ }
459
+
460
+ return $content;
461
+ }
462
+
463
+ /**
464
+ * Returns true if the script name can be used
465
+ *
466
+ * @return bool
467
+ */
468
+ private function checkMainScript()
469
+ {
470
+ if (null !== $this->script) {
471
+ // Allow an application to set -- for standard input
472
+ return file_exists($this->script) || '--' === $this->script;
473
+ }
474
+
475
+ if (file_exists($this->script = $_SERVER['argv'][0])) {
476
+ return true;
477
+ }
478
+
479
+ // Use a backtrace to resolve Phar and chdir issues
480
+ $options = PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : false;
481
+ $trace = debug_backtrace($options);
482
+
483
+ if (($main = end($trace)) && isset($main['file'])) {
484
+ return file_exists($this->script = $main['file']);
485
+ }
486
+
487
+ return false;
488
+ }
489
+
490
+ /**
491
+ * Adds restart settings to the environment
492
+ *
493
+ * @param string $envArgs
494
+ */
495
+ private function setEnvRestartSettings($envArgs)
496
+ {
497
+ $settings = array(
498
+ php_ini_loaded_file(),
499
+ $envArgs[2],
500
+ $envArgs[3],
501
+ $envArgs[4],
502
+ getenv($this->envOriginalInis),
503
+ self::$skipped,
504
+ );
505
+
506
+ Process::setEnv(self::RESTART_SETTINGS, implode('|', $settings));
507
+ }
508
+
509
+ /**
510
+ * Syncs settings and the environment if called with existing settings
511
+ *
512
+ * @param array $settings
513
+ */
514
+ private function syncSettings(array $settings)
515
+ {
516
+ if (false === getenv($this->envOriginalInis)) {
517
+ // Called by another app, so make original inis available
518
+ Process::setEnv($this->envOriginalInis, implode(PATH_SEPARATOR, $settings['inis']));
519
+ }
520
+
521
+ self::$skipped = $settings['skipped'];
522
+ $this->notify(Status::INFO, 'Process called with existing restart settings');
523
+ }
524
+
525
+ /**
526
+ * Returns true if there are scanned inis and PHP is able to report them
527
+ *
528
+ * php_ini_scanned_files will fail when PHP_CONFIG_FILE_SCAN_DIR is empty.
529
+ * Fixed in 7.1.13 and 7.2.1
530
+ *
531
+ * @return bool
532
+ */
533
+ private function checkScanDirConfig()
534
+ {
535
+ return !(getenv('PHP_INI_SCAN_DIR')
536
+ && !PHP_CONFIG_FILE_SCAN_DIR
537
+ && (PHP_VERSION_ID < 70113
538
+ || PHP_VERSION_ID === 70200));
539
+ }
540
+ }