From 6c3fa2f4e6f795cd76aea57639220297e64c2b13 Mon Sep 17 00:00:00 2001 From: Andreas Heigl Date: Sat, 9 May 2015 08:18:31 +0200 Subject: [PATCH 001/277] Adds possibility to search for commenter-email THis commit enables us to search for bugs that have been commented using a certain email-address --- include/query.php | 10 +++++++++- www/search.php | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/include/query.php b/include/query.php index 5f8d4358..1617b7b8 100644 --- a/include/query.php +++ b/include/query.php @@ -50,6 +50,7 @@ $author_email = !empty($_GET['author_email']) ? spam_protect($_GET['author_email'], 'reverse') : ''; $package_name = (isset($_GET['package_name']) && is_array($_GET['package_name'])) ? $_GET['package_name'] : array(); $package_nname = (isset($_GET['package_nname']) && is_array($_GET['package_nname'])) ? $_GET['package_nname'] : array(); +$commented_by = !empty($_GET['commented_by']) ? spam_protect($_GET['commented_by'], 'reverse') : ''; if (isset($_GET['cmd']) && $_GET['cmd'] == 'display') { @@ -64,7 +65,11 @@ if (in_array($order_by, array('votes_count', 'avg_score'))) { $query .= 'LEFT JOIN bugdb_votes v ON bugdb.id = v.bug'; - } + } + + if ($commented_by != '') { + $query .= 'LEFT JOIN bugdb_comments c ON bugdb.id = c.bug'; + } $where_clause = ' WHERE 1 = 1 '; @@ -196,6 +201,9 @@ if ($author_email != '') { $where_clause .= ' AND bugdb.email = ' . $dbh->quote($author_email); } + if ($commented_by != '') { + $where_clause .= ' AND c.email = ' . $dbh->quote($commented_by); + } $where_clause .= ' AND (1=1'; diff --git a/www/search.php b/www/search.php index 727c41cd..7e976da8 100644 --- a/www/search.php +++ b/www/search.php @@ -77,7 +77,8 @@ 'cve_id_not' => $cve_id_not, 'patch' => urlencode($patch), 'pull' => urlencode($pull), - 'assign' => urlencode($assign) + 'assign' => urlencode($assign), + 'commented_by' => urlencode($commented_by), ]; if ($is_security_developer) { @@ -334,6 +335,11 @@ Return only bugs with a pull request > + + Commented by + Return bugs that have been commented by + + From a25244842d0df20cce89e2947a02cc1bc2fb7bd2 Mon Sep 17 00:00:00 2001 From: Sobak Date: Thu, 17 Sep 2015 01:25:23 +0200 Subject: [PATCH 002/277] Slightly reword to reduce ammount of duplicated data --- www/report.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/report.php b/www/report.php index ad9ca151..1c325f04 100644 --- a/www/report.php +++ b/www/report.php @@ -506,8 +506,8 @@ Expected result:

+ Skip if irrelevant. What do you expect to happen or see when you run the test script above? - Leave it empty if irrelevant (e.g. you're going to duplicate description here).

@@ -519,9 +519,9 @@ Actual result:

+ Skip if irrelevant. This could be a backtrace for example. Try to keep it as short as possible without leaving anything relevant out. - Leave it empty if irrelevant (e.g. you're going to duplicate description here).

From 432aab7e2c1f1fa41f78dfeeb55aa022a9fc9190 Mon Sep 17 00:00:00 2001 From: Sobak Date: Thu, 17 Sep 2015 08:44:53 +0200 Subject: [PATCH 003/277] Provide search results in RSS format Actually it should already exist - there is reference for it in search results' HTML and also 'format' var in the URL. It's going to be used e.g. on doc.php.net --- templates/search-rdf.php | 73 ++++++++++++++++++++++++++++++++++ templates/search-rss2.php | 25 ++++++++++++ www/rss/search.php | 83 ++++++--------------------------------- 3 files changed, 110 insertions(+), 71 deletions(-) create mode 100644 templates/search-rdf.php create mode 100644 templates/search-rss2.php diff --git a/templates/search-rdf.php b/templates/search-rdf.php new file mode 100644 index 00000000..fd67fdfb --- /dev/null +++ b/templates/search-rdf.php @@ -0,0 +1,73 @@ + + +'; +echo "\n \n"; +echo " {$siteBig} Bug Search Results\n"; +echo " {$site_method}://{$site_url}{$basedir}/rss/search.php?" , clean(http_build_query($_GET)) , "\n"; +echo " Search Results\n"; +echo " en-us\n"; +echo " {$site}-webmaster@lists.php.net\n"; +echo " {$site}-webmaster@lists.php.net\n"; +echo " \n"; +echo " hourly\n"; +echo " 1\n"; +echo " 2000-01-01T12:00+00:00\n"; +echo ' + +'; + +$items = ''; +if ($total_rows > 0) { + foreach ($res->fetchAll(MDB2_FETCHMODE_ASSOC) as $row) { + $desc = "{$row['package_name']} ({$row['bug_type']})\nReported by "; + if (preg_match('/@php.net$/i', $row['email'])) { + $desc .= substr($row['email'], 0, strpos($row['email'], '@')) ."\n"; + } else { + $desc .= substr($row['email'], 0, strpos($row['email'], '@')) . "@...\n"; + } + $desc .= date(DATE_ATOM, $row['submitted']) . "\n"; + $desc .= "PHP: {$row['php_version']}, OS: {$row['php_os']}\n\n"; + $desc .= $row['ldesc']; + $desc = '
' . clean($desc) . '
'; + + echo " \n"; + $items .= " \n"; + $items .= ' ' . clean("{$row['bug_type']} {$row['id']} [{$row['status']}] {$row['sdesc']}") . "\n"; + $items .= " {$site_method}://{$site_url}{$basedir}/{$row['id']}\n"; + $items .= ' \n"; + $items .= ' \n"; + if (!$row['unchanged']) { + $items .= ' ' . date(DATE_ATOM, $row['submitted']) . "\n"; + } else { + $items .= ' ' . date(DATE_ATOM, $row['modified']) . "\n"; + } + $items .= ' ' . clean(spam_protect($row['email'])) . "\n"; + $items .= ' ' . clean($row['package_name']) . ' ' . clean($row['bug_type']) . "\n"; + $items .= " \n"; + } +} else { + $warnings[] = "No bugs matched your criteria"; +} + +echo <<< DATA +
+
+
+ + + {$siteBig} Bugs + {$site_method}://{$site_url}{$basedir}/images/{$site}-logo.gif + {$site_method}://{$site_url}{$basedir} + + +{$items} +DATA; +?> +
\ No newline at end of file diff --git a/templates/search-rss2.php b/templates/search-rss2.php new file mode 100644 index 00000000..3a7b0b06 --- /dev/null +++ b/templates/search-rss2.php @@ -0,0 +1,25 @@ +' . "\n"; +?> + + + <?php echo $siteBig; ?> Bug Search Results +{$site_method}://{$site_url}{$basedir}/rss/search.php?" , clean(http_build_query($_GET)) , "\n"; ?> + Search Results + 0) { + foreach ($res->fetchAll(MDB2_FETCHMODE_ASSOC) as $row) { + echo " \n"; + echo ' ' . clean($row['sdesc']) . "\n"; + echo " {$site_method}://{$site_url}{$basedir}/{$row['id']}\n"; + echo " {$row['status']}\n"; + echo ' ' . date(DATE_RSS, $row['submitted']) . "\n"; + echo " \n"; + } +} else { + $warnings[] = "No bugs matched your criteria"; +} +?> + + \ No newline at end of file diff --git a/www/rss/search.php b/www/rss/search.php index 38556423..c5b6d6cb 100644 --- a/www/rss/search.php +++ b/www/rss/search.php @@ -13,8 +13,16 @@ * ported by Gregory Beaver */ +$format = (isset($_GET['format']) && $_GET['format'] === 'rss2') ? 'rss2' : 'rdf'; + // Maximum number of bugs to return -define ('MAX_BUGS_RETURN', 150); +if ($format === 'rss2') { + // RSS channel shows way more data (e.g. no bug description) thus + // we can fetch more rows + define ('MAX_BUGS_RETURN', 500); +} else { + define ('MAX_BUGS_RETURN', 150); +} // Obtain common includes require_once '../../include/prepend.php'; @@ -28,79 +36,12 @@ $total_rows = $dbh->prepare('SELECT FOUND_ROWS()')->execute()->fetchOne(); } -header('Content-type: text/xml'); - -echo ' - -'; -echo "\n \n"; -echo " {$siteBig} Bug Search Results\n"; -echo " {$site_method}://{$site_url}{$basedir}/rss/search.php?" , clean(http_build_query($_GET)) , "\n"; -echo " Search Results\n"; -echo " en-us\n"; -echo " {$site}-webmaster@lists.php.net\n"; -echo " {$site}-webmaster@lists.php.net\n"; -echo " \n"; -echo " hourly\n"; -echo " 1\n"; -echo " 2000-01-01T12:00+00:00\n"; -echo ' - -'; - -$items = ''; -if ($total_rows > 0) { - foreach ($res->fetchAll(MDB2_FETCHMODE_ASSOC) as $row) { - $desc = "{$row['package_name']} ({$row['bug_type']})\nReported by "; - if (preg_match('/@php.net$/i', $row['email'])) { - $desc .= substr($row['email'], 0, strpos($row['email'], '@')) ."\n"; - } else { - $desc .= substr($row['email'], 0, strpos($row['email'], '@')) . "@...\n"; - } - $desc .= date(DATE_ATOM, $row['submitted']) . "\n"; - $desc .= "PHP: {$row['php_version']}, OS: {$row['php_os']}\n\n"; - $desc .= $row['ldesc']; - $desc = '
' . clean($desc) . '
'; - - echo " \n"; - $items .= " \n"; - $items .= ' ' . clean("{$row['bug_type']} {$row['id']} [{$row['status']}] {$row['sdesc']}") . "\n"; - $items .= " {$site_method}://{$site_url}{$basedir}/{$row['id']}\n"; - $items .= ' \n"; - $items .= ' \n"; - if (!$row['unchanged']) { - $items .= ' ' . date(DATE_ATOM, $row['submitted']) . "\n"; - } else { - $items .= ' ' . date(DATE_ATOM, $row['modified']) . "\n"; - } - $items .= ' ' . clean(spam_protect($row['email'])) . "\n"; - $items .= ' ' . clean($row['package_name']) . ' ' . clean($row['bug_type']) . "\n"; - $items .= " \n"; - } +if ($format === 'rss2') { + require "{$ROOT_DIR}/templates/search-rss2.php"; } else { - $warnings[] = "No bugs matched your criteria"; + require "{$ROOT_DIR}/templates/search-rdf.php"; } -echo <<< DATA -
-
-
- - - {$siteBig} Bugs - {$site_method}://{$site_url}{$basedir}/images/{$site}-logo.gif - {$site_method}://{$site_url}{$basedir} - - -{$items} -DATA; -?> -
- 0) { echo " -

Generating backtrace, without compiler, on Win32

+

Generating backtrace, without compiler, on Windows

You'll need:

-

After that any process crashing in your system, including PHP, will leave +

After that any process crashing in your system, including PHP, will leave its core file in the directory you've specified in core_pattern.

Once you have the core file:

@@ -171,7 +171,7 @@ $15 = 0x816cfc4 "result_error" (gdb) print (char *)executor_globals.active_op_array->filename $16 = 0x816afbc "/home/yohgaki/php/DEV/segfault.php" -(gdb) +(gdb)
diff --git a/www/bugs-getting-valgrind-log.php b/www/bugs-getting-valgrind-log.php index 1a5c821a..3dc9b3ef 100644 --- a/www/bugs-getting-valgrind-log.php +++ b/www/bugs-getting-valgrind-log.php @@ -14,22 +14,22 @@

Important!

To get a meaningful log you must have -PHP configured with --enable-debug +PHP configured with --enable-debug and disable Zend memory manager.

Disabling Zend MM

- Zend Engine uses its own routines to optimize memory management, + Zend Engine uses its own routines to optimize memory management, but because of this valgrind cannot see most of the memory issues. You must disable Zend memory manager before running PHP with valgrind. In order to do this you need to set USE_ZEND_ALLOC environment - variable to 0. + variable to 0.

Use -

export USE_ZEND_ALLOC=0
or -
setenv USE_ZEND_ALLOC 0
(the syntax depends on +
export USE_ZEND_ALLOC=0
or +
setenv USE_ZEND_ALLOC 0
(the syntax depends on what your shell supports).

@@ -42,8 +42,8 @@

To correctly show the stack frames for extensions compiled as shared libraries, set: -

export ZEND_DONT_UNLOAD_MODULES=1
or -
setenv ZEND_DONT_UNLOAD_MODULES 1
(the syntax depends on +
export ZEND_DONT_UNLOAD_MODULES=1
or +
setenv ZEND_DONT_UNLOAD_MODULES 1
(the syntax depends on what your shell supports).

@@ -54,8 +54,8 @@

Running PHP CLI, Built-in Web Server or PHP CGI through valgrind

- To generate the valgrind log using PHP CLI/CGI, - you need to execute the following command: + To generate the valgrind log using PHP CLI/CGI, + you need to execute the following command:

@@ -77,9 +77,9 @@
 

Running PHP Apache module through valgrind

- If you compiled PHP and Apache statically, make sure the Apache binary - is not stripped after make install, otherwise you lose - the required debug info. To check it, run file /path/to/httpd, + If you compiled PHP and Apache statically, make sure the Apache binary + is not stripped after make install, otherwise you lose + the required debug info. To check it, run file /path/to/httpd, it should output something like this (notice "not stripped"):

@@ -90,8 +90,8 @@
 

- To generate the valgrind log using PHP as Apache module, - you need to run the Apache itself under valgrind: + To generate the valgrind log using PHP as Apache module, + you need to run the Apache itself under valgrind:

diff --git a/www/css/style.css b/www/css/style.css
index 45951b70..6c6d5da0 100644
--- a/www/css/style.css
+++ b/www/css/style.css
@@ -198,7 +198,7 @@ td.form-input_center {
 	background-color: #e8e8e8;
 }
 
-td.form-input input, 
+td.form-input input,
 td.form-input_center input {
 	font-size: 90%;
 }
@@ -801,7 +801,7 @@ td.search-next {
 	overflow: auto;
 	margin: -6px 6px 6px -6px;
 	/* IE6 specific: */
-	_height:350px; 
+	_height:350px;
 	_margin:0;
 	_overflow-x:hidden;
 }
diff --git a/www/fix.php b/www/fix.php
index 825e9ff1..2834805e 100644
--- a/www/fix.php
+++ b/www/fix.php
@@ -93,7 +93,7 @@
 		
 			Note:
 			
-		 
+		
 	
 	
 
diff --git a/www/gh-pull-add.php b/www/gh-pull-add.php
index f9babd00..8a32472b 100644
--- a/www/gh-pull-add.php
+++ b/www/gh-pull-add.php
@@ -118,7 +118,7 @@
 
 	$res = bugs_add_comment($bug_id, $auth_user->email, $auth_user->name, $text, 'patch');
 
-	// Send emails 
+	// Send emails
 	mail_bug_updates($buginfo, $buginfo, $auth_user->email, $text, 4, $bug_id);
  */
 	$pulls = $pullinfo->listPulls($bug_id);
diff --git a/www/how-to-report.php b/www/how-to-report.php
index 2b84efd9..68bf6ed8 100644
--- a/www/how-to-report.php
+++ b/www/how-to-report.php
@@ -88,7 +88,7 @@
 		can be found here for *NIX users and
 		here for Windows users.
 	
-	
  • +
  • Valgrind log can be also very useful. See instructions how to generate it.
  • @@ -126,7 +126,7 @@ To Ask Questions The Smart Way
  • - mozilla.org's + mozilla.org's bug writing guidelines
  • diff --git a/www/index.php b/www/index.php index 15acf271..bd91baa8 100644 --- a/www/index.php +++ b/www/index.php @@ -42,7 +42,7 @@
    • - Used the form above or our advanced search page + Used the form above or our advanced search page to make sure nobody has reported the bug already.
    • @@ -58,13 +58,13 @@
    • Read the security guidelines, if you think an issue might be security related.
    • - +
    • - See how to get a backtrace in case of a crash: + See how to get a backtrace in case of a crash: for *NIX and for Windows.
    • - +
    • Make sure it isn't a support question. For support, see the support page. @@ -80,12 +80,12 @@

      You can search all of the bugs that have been reported on our advanced search page, or use the form -at the top of the page for a basic default search. Read the -search howto for instructions on +at the top of the page for a basic default search. Read the +search howto for instructions on how search works.

      If you have 10 minutes to kill and you want to help us out, grab a -random open bug and see if you can help resolve it. We have made it +random open bug and see if you can help resolve it. We have made it easy. Hit :///random to go directly to a random open bug.

      @@ -94,7 +94,7 @@
        '&bug_type=All', 'Most recent open bugs (all) with patch or pull request' => '&bug_type=All&patch=Y&pull=Y', diff --git a/www/js/Markdown.Converter.js b/www/js/Markdown.Converter.js index d01f1ab9..cfec79ef 100644 --- a/www/js/Markdown.Converter.js +++ b/www/js/Markdown.Converter.js @@ -4,7 +4,7 @@ if (typeof exports === "object" && typeof require === "function") // we're in a Markdown = exports; else Markdown = {}; - + // The following text is included for historical reasons, but should // be taken with a pinch of salt; it's not all true anymore. @@ -133,7 +133,7 @@ else // Don't do that. if (g_urls) throw new Error("Recursive call to converter.makeHtml"); - + // Create the private state objects. g_urls = new SaveHash(); g_titles = new SaveHash(); @@ -305,7 +305,7 @@ else text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm, hashElement); // Special case just for
        . It was easier to make a special case than - // to make the other regex more complicated. + // to make the other regex more complicated. /* text = text.replace(/ @@ -425,9 +425,9 @@ else // Must come after _DoAnchors(), because you can use < and > // delimiters in inline links like [this](). text = _DoAutoLinks(text); - + text = text.replace(/~P/g, "://"); // put in place to prevent autolinking; reset now - + text = _EncodeAmpsAndAngles(text); text = _DoItalicsAndBold(text); @@ -443,7 +443,7 @@ else // don't conflict with their use in Markdown for code, italics and strong. // - // Build a regex to find HTML tags and comments. See Friedl's + // Build a regex to find HTML tags and comments. See Friedl's // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. // SE: changed the comment part of the regex @@ -517,7 +517,7 @@ else | [^()] )*? - )>? + )>? [ \t]* ( // $5 (['"]) // quote char = $6 @@ -656,7 +656,7 @@ else return text; } - + function attributeEncode(text) { // unconditionally replace angle brackets here -- what ends up in an attribute (e.g. alt or title) // never makes sense to have verbatim HTML in it (and the sanitizer would totally break it) @@ -689,7 +689,7 @@ else return whole_match; } } - + alt_text = escapeCharacters(attributeEncode(alt_text), "*_[]()"); url = escapeCharacters(url, "*_"); var result = "\""` blocks. - // + // /* text = text.replace(/ @@ -968,26 +968,26 @@ else function _DoCodeSpans(text) { // // * Backtick quotes are used for spans. - // + // // * You can use multiple backticks as the delimiters if you want to // include literal backticks in the code span. So, this input: - // + // // Just type ``foo `bar` baz`` at the prompt. - // + // // Will translate to: - // + // //

        Just type foo `bar` baz at the prompt.

        - // + // // There's no arbitrary limit to the number of backticks you // can use as delimters. If you need three consecutive backticks // in your code, use four for delimiters, etc. // // * You can use spaces to get literal backticks at the edges: - // + // // ... type `` `bar` `` ... - // + // // Turns to: - // + // // ... type `bar` ... // @@ -1120,7 +1120,7 @@ else var grafs = text.split(/\n{2,}/g); var grafsOut = []; - + var markerRe = /~K(\d+)K/; // @@ -1201,11 +1201,11 @@ else // *except* for the case // automatically add < and > around unadorned raw hyperlinks - // must be preceded by space/BOF and followed by non-word/EOF character + // must be preceded by space/BOF and followed by non-word/EOF character text = text.replace(/(^|\s)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]])($|\W)/gi, "$1<$2$3>$4"); // autolink anything like - + var replacer = function (wholematch, m1) { return "" + pluginHooks.plainLinkText(m1) + ""; } text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer); @@ -1287,7 +1287,7 @@ else var _problemUrlChars = /(?:["'*()[\]:]|~D)/g; - // hex-encodes some unusual "problem" chars in URLs to avoid URL detection problems + // hex-encodes some unusual "problem" chars in URLs to avoid URL detection problems function encodeProblemUrlChars(url) { if (!url) return ""; diff --git a/www/js/Markdown.Sanitizer.js b/www/js/Markdown.Sanitizer.js index cc5826fa..c3283e59 100644 --- a/www/js/Markdown.Sanitizer.js +++ b/www/js/Markdown.Sanitizer.js @@ -7,7 +7,7 @@ output = window.Markdown; Converter = output.Converter; } - + output.getSanitizingConverter = function () { var converter = new Converter(); converter.hooks.chain("postConversion", sanitizeHtml); @@ -37,9 +37,9 @@ /// /// attempt to balance HTML tags in the html string /// by removing any unmatched opening or closing tags - /// IMPORTANT: we *assume* HTML has *already* been + /// IMPORTANT: we *assume* HTML has *already* been /// sanitized and is safe/sane before balancing! - /// + /// /// adapted from CODESNIPPET: A8591DBA-D1D3-11DE-947C-BA5556D89593 /// function balanceTags(html) { diff --git a/www/js/search.js b/www/js/search.js index 4692541c..da9496bc 100644 --- a/www/js/search.js +++ b/www/js/search.js @@ -7,8 +7,8 @@ var fnFormatSearchResult = function(value, data, currentValue) { return ' ' + listing; }; -$('#assigned_user').autocomplete({ - minChars:2, +$('#assigned_user').autocomplete({ + minChars:2, maxHeight:400, width:300, fnFormatResult: fnFormatSearchResult, diff --git a/www/login.php b/www/login.php index dc7bd38f..3aec62ad 100644 --- a/www/login.php +++ b/www/login.php @@ -12,7 +12,7 @@ if (isset($_POST['user'])) { $referer = $_POST['referer']; - + bugs_authenticate($user, $pwd, $logged_in, $user_flags); if ($logged_in === 'developer') { diff --git a/www/lstats.php b/www/lstats.php index 4ae69a5d..d8a8ae2a 100644 --- a/www/lstats.php +++ b/www/lstats.php @@ -10,7 +10,7 @@ function status_print ($status, $num, $width, $align = STR_PAD_LEFT) function get_status_count ($status, $category = '') { global $phpver, $dbh; - + $query = "SELECT count(id) from bugdb WHERE"; if ($phpver > 0) { @@ -33,20 +33,20 @@ function get_status_count ($status, $category = '') return $row[0]; } -// Input +// Input $phpver = (isset($_GET['phpver']) ? (int) $_GET['phpver'] : false); if (!$phpver || ($phpver !== 5 && $phpver !== 7)) { - echo "

        Bug stats for both PHP 5 and PHP 7:

        \n
        \n";	
        +	echo "

        Bug stats for both PHP 5 and PHP 7:

        \n
        \n";
         } else {
        -	echo "

        Bug stats for PHP $phpver:

        \n
        \n";	
        +	echo "

        Bug stats for PHP $phpver:

        \n
        \n";
         }
         
         if (isset($_GET['per_category']))
         {
         	$project = !empty($_GET['project']) ? $_GET['project'] : false;
         	$pseudo_pkgs = get_pseudo_packages($project);
        -	
        +
         	$totals = array();
         	foreach ($pseudo_pkgs as $category => $data) {
         		$count = get_status_count ("status NOT IN('to be documented', 'closed', 'not a bug', 'duplicate', 'wont fix', 'no feedback')", $category);
        @@ -58,7 +58,7 @@ function get_status_count ($status, $category = '')
         	foreach ($totals as $category => $total) {
         		status_print($category, $total, 40);
         	}
        -	
        +
         } else {
         
         	foreach ($tla as $status => $short) {
        diff --git a/www/patch-add.php b/www/patch-add.php
        index 428e29bd..92bbac1a 100644
        --- a/www/patch-add.php
        +++ b/www/patch-add.php
        @@ -140,7 +140,7 @@
         
         	$res = bugs_add_comment($bug_id, $auth_user->email, $auth_user->name, $text, 'patch');
         
        -	// Send emails 
        +	// Send emails
         	mail_bug_updates($buginfo, $buginfo, $auth_user->email, $text, 4, $bug_id);
         
         	$patches = $patchinfo->listPatches($bug_id);
        diff --git a/www/quick-fix-desc.php b/www/quick-fix-desc.php
        index 99c019f2..5d6c67b2 100644
        --- a/www/quick-fix-desc.php
        +++ b/www/quick-fix-desc.php
        @@ -10,9 +10,9 @@
         // Authenticate
         bugs_authenticate($user, $pw, $logged_in, $user_flags);
         
        -response_header('Quick Fix Descriptions'); 
        +response_header('Quick Fix Descriptions');
         
        -?> 
        +?>
         ";
         		}
         	}
        -} 
        +}
         ?>
         
        diff --git a/www/report.php b/www/report.php index 79d92264..1b9606e3 100644 --- a/www/report.php +++ b/www/report.php @@ -1,9 +1,9 @@ - - + Package affected: @@ -525,7 +525,7 @@ -getOperation(); $_SESSION['answer'] = $numeralCaptcha->getAnswer(); if (!empty($_POST['captcha']) && empty($ok_to_submit_report)) { diff --git a/www/rpc.php b/www/rpc.php index 4a00a7c4..0a97a497 100644 --- a/www/rpc.php +++ b/www/rpc.php @@ -42,7 +42,7 @@ if (!bugs_has_access($bug_id, $bug, $pwd, $user_flags)) { echo json_encode(array('result' => array('error' => 'No access to bug'))); exit; -} +} if (!empty($_POST['ncomment']) && !empty($_POST['user'])) { $user = htmlspecialchars(trim($_POST['user'])); @@ -55,15 +55,15 @@ if ($res) { /* Close the bug report as requested if it is not already closed */ if (!empty($_POST['status']) - && $bug['status'] !== 'Closed' + && $bug['status'] !== 'Closed' && $_POST['status'] === 'Closed') { /* Change the bug status to Closed */ bugs_status_change($bug_id, 'Closed'); - + $in = $bug; /* Just change the bug status */ $in['status'] = $_POST['status']; - + $changed = bug_diff($bug, $in); if (!empty($changed)) { $log_comment = bug_diff_render_html($changed); @@ -72,11 +72,11 @@ $res = bugs_add_comment($bug_id, $from, '', $log_comment, 'log'); } } - + /* Send a mail notification when automatically closing a bug */ mail_bug_updates($bug, $in, $from, $ncomment, 1, $bug_id); } - + echo json_encode(array('result' => array('status' => $bug))); exit; } else { diff --git a/www/rss/rdf.php b/www/rss/rdf.php index 9a50853f..3982af1b 100644 --- a/www/rss/rdf.php +++ b/www/rss/rdf.php @@ -48,7 +48,7 @@ print ''; ?> - # - +
        ]]>
        ]]> diff --git a/www/rss/rss.php b/www/rss/rss.php index 91298b76..b8427260 100644 --- a/www/rss/rss.php +++ b/www/rss/rss.php @@ -11,7 +11,7 @@ $desc .= "PHP: {$bug['php_version']}, OS: {$bug['php_os']}\n\n"; $desc .= $bug['ldesc']; $desc = '
        ' . clean($desc) . '
        '; - + ?> @@ -35,7 +35,7 @@ <?php echo clean($comment['email'] . " [$displayts]"); ?> ', clean($comment['comment']), '
        '; ?>]]> - + diff --git a/www/search-howto.php b/www/search-howto.php index 52bbc3ae..30a9fabb 100755 --- a/www/search-howto.php +++ b/www/search-howto.php @@ -14,24 +14,24 @@

        This HOWTO will allow for a useful experience while scouring the bug database. -Do note that a lot of information is entered in by the general public and +Do note that a lot of information is entered in by the general public and therefore cannot be fully trusted. Also, the information contained within a bug report is what setup found the bug, so other setups may apply. -

        - +

        +

        Basic Search

        Within every / webpage header is a search box, this is the basic search option. You may enter in a numeric bug ID to redirect to that bugs page or -enter in a search term to perform a default bug search. +enter in a search term to perform a default bug search. Load the advanced search to view the default values.

        Advanced Search

        -Some explanations for most of the PHP bugs advanced search +Some explanations for most of the PHP bugs advanced search options.

        @@ -51,7 +51,7 @@
      • any : One or more (any) of the search terms may be present.
      • raw : Allows full use of MySQL's - FULLTEXT + FULLTEXT boolean search operators.
      • @@ -61,8 +61,8 @@ not caring which shows up. Or a name that has changed in PHP 5 from PHP 4. Use of all makes sense if you require every term in your results, as this can provide precise searching. The raw - option is for custom searches, like you might require one term but also want - to disallow another from the result. Also, adding optional terms always + option is for custom searches, like you might require one term but also want + to disallow another from the result. Also, adding optional terms always affects relevancy/order. @@ -73,7 +73,7 @@ Here are a few explanations:
        • - Open: This also includes assigned, analyzed, + Open: This also includes assigned, analyzed, critical, and verified bugs. (default)
        • @@ -88,8 +88,8 @@ date as public comments do not.
        • - Stale: Bugs last commented on at least 30 days ago that are not - closed, duplicates, or not-a-bug. Only developers and the original author can affect + Stale: Bugs last commented on at least 30 days ago that are not + closed, duplicates, or not-a-bug. Only developers and the original author can affect this date as public comments do not count.
        • All: All types, even not-a-bug.
        • @@ -101,7 +101,7 @@
        @@ -110,9 +110,9 @@ @@ -122,7 +122,7 @@
        Category Bugs are categorized although sometimes it might seem like a bug could be in - multiple categories. You may choose a specific category or allow any, and + multiple categories. You may choose a specific category or allow any, and also disallow certain categories. If you're unable to locate a bug, consider trying a feature request or any status.
        OS - Bugs that may be specific to an operating system. This value is entered in by the + Bugs that may be specific to an operating system. This value is entered in by the reporter as the OS they used while finding the bug so this may or may not have meaning. - Also, the value isn't regulated so for example Windows may be written as Win32, Win, + Also, the value isn't regulated so for example Windows may be written as Win32, Win, Windows, Win98, NT, etc. Or perhaps a distribution name rather than simply Linux. The query uses a SQL LIKE statement like so: '%$os%'. Version Limit bugs to a specific version of PHP. A one character integer of 3, 4 or - 5 is standard. Entering a length greater than one will perform a SQL LIKE + 5 is standard. Entering a length greater than one will perform a SQL LIKE statement like so: '$version%'. Defaults to both 4 and 5. @@ -147,8 +147,8 @@
        Date - Limit bugs that were reported by a specific time period. This is not only the - amount of time since a comment or developer remark was last made, but this is + Limit bugs that were reported by a specific time period. This is not only the + amount of time since a comment or developer remark was last made, but this is the time when the bug was originally reported. From 8c52db690562683212a322d45b5f11ca36f86f0d Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 17 Oct 2018 15:51:47 +0200 Subject: [PATCH 102/277] Sync leading and final newlines This patch adds missing newlines, trims multiple redundant final newlines into a single one and trims leading newlines at the beginning of the files. According to POSIX, a line is a sequence of zero or more non-'' characters plus a terminating '' character. [1] Files should normally have at least one final newline character. C89 [2] and later standards [3] mention a final newline: "A source file that is not empty shall end in a new-line character, which shall not be immediately preceded by a backslash character." Although it is not mandatory for all files to have a final newline fixed, a more consistent and homogeneous approach brings less of commit differences issues and a better development experience in certain text editors and IDEs. [1] http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206 [2] https://port70.net/~nsz/c/c89/c89-draft.html#2.1.1.2 [3] https://port70.net/~nsz/c/c99/n1256.html#5.1.1.2 --- include/trusted-devs.php | 1 - sql/bugs.sql | 2 -- templates/search-rdf.php | 2 +- templates/search-rss2.php | 2 +- www/bugs-generating-backtrace-win32.php | 1 - www/css/style.css | 2 +- www/js/jquery.cookie.js | 2 +- www/stats.php | 1 - 8 files changed, 4 insertions(+), 9 deletions(-) diff --git a/include/trusted-devs.php b/include/trusted-devs.php index b7ced1fc..5a045515 100644 --- a/include/trusted-devs.php +++ b/include/trusted-devs.php @@ -73,4 +73,3 @@ ); $security_developers = array_merge($security_developers, $security_distro_people); - diff --git a/sql/bugs.sql b/sql/bugs.sql index 05b16167..e7ad186d 100644 --- a/sql/bugs.sql +++ b/sql/bugs.sql @@ -1,4 +1,3 @@ - -- ts1 bug created date -- ts2 bug last updated date -- passwd user password @@ -119,4 +118,3 @@ CREATE TABLE bugdb_pulls ( # Default pseudo packages (common for all projects) INSERT INTO bugdb_pseudo_packages SET id = '1', parent = '0', name = 'Web Site', long_name = 'Web Site', project = ''; INSERT INTO bugdb_pseudo_packages SET id = '2', parent = '1', name = 'Bug System', long_name = 'Bug System', project = ''; - diff --git a/templates/search-rdf.php b/templates/search-rdf.php index fa1edbc5..68d3e93d 100644 --- a/templates/search-rdf.php +++ b/templates/search-rdf.php @@ -70,4 +70,4 @@ {$items} DATA; ?> - \ No newline at end of file + diff --git a/templates/search-rss2.php b/templates/search-rss2.php index a0206846..f7043028 100644 --- a/templates/search-rss2.php +++ b/templates/search-rss2.php @@ -22,4 +22,4 @@ } ?> - \ No newline at end of file + diff --git a/www/bugs-generating-backtrace-win32.php b/www/bugs-generating-backtrace-win32.php index 18acf3d2..ee15f67e 100644 --- a/www/bugs-generating-backtrace-win32.php +++ b/www/bugs-generating-backtrace-win32.php @@ -97,4 +97,3 @@

        What we need is the backtrace itself which can be found under "Thread X - System ID XXX".

        ' . "\n"; return $stat_head; } - From 7b505fa99b01ef23d973b17c6ff5a144a9da68b4 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 6 Aug 2018 08:22:26 +0200 Subject: [PATCH 103/277] Apply line-height CSS style to all pre tags This improves readability when
         tags are used.
        ---
         www/css/style.css | 5 ++++-
         1 file changed, 4 insertions(+), 1 deletion(-)
        
        diff --git a/www/css/style.css b/www/css/style.css
        index d153e697..c9dcbe9e 100644
        --- a/www/css/style.css
        +++ b/www/css/style.css
        @@ -424,6 +424,10 @@ code, pre {
         	font-size: 13px;
         }
         
        +pre {
        +	line-height: 1.4;
        +}
        +
         h1 {
         	font-size: 140%;
         	font-weight: bold;
        @@ -530,7 +534,6 @@ td.content table {
         	margin-left: 10px;
         	white-space:pre-wrap;
         	font-size: 13px;
        -	line-height: 1.4;
         }
         .lusersearch .note {
         	width:100%;
        
        From 70d522f5a04da22c68ab62fbd8c63edf27f33987 Mon Sep 17 00:00:00 2001
        From: Peter Kokot 
        Date: Mon, 22 Oct 2018 23:58:27 +0200
        Subject: [PATCH 104/277] Update README with installation info
        
        ---
         README.md | 43 +++++++++++++++++++++++++++++++++++--------
         1 file changed, 35 insertions(+), 8 deletions(-)
        
        diff --git a/README.md b/README.md
        index 61f351d2..97bdf587 100644
        --- a/README.md
        +++ b/README.md
        @@ -1,8 +1,35 @@
        -	- Text_Diff
        -	- HTTP_Upload
        -
        -Installation
        -============
        -1. Copy `local_config.php.sample` to `local_config.php` and modify accordingly
        -2. Install all required packages:
        -`pear install Text_CAPTCHA_Numeral Text_Diff HTTP_Upload`
        +# PHP Bug Tracking System
        +
        +This is a unified bug tracking system for PHP hosted online at
        +[bugs.php.net](https://bugs.php.net).
        +
        +## Requirements
        +
        +- PHP 5.4+
        +- ext/pdo
        +- ext/pdo_mysql
        +- ext/openssl (for https:// fopen wrapper)
        +- PEAR packages:
        +  - Text_CAPTCHA_Numeral
        +  - Text_Diff
        +  - HTTP_Upload (1.0.0b4 or later)
        +
        +## Local installation
        +
        +* Copy configuration and modify it accordingly for your local system:
        +
        +```bash
        +cp local_config.php.sample local_config.php
        +```
        +
        +* Install required dependencies using PEAR:
        +
        +```bash
        +pear channel-update pear.php.net
        +pear install --alldeps Text_CAPTCHA_Numeral Text_Diff HTTP_Upload-1.0.0b4
        +```
        +
        +* Database:
        +
        +Create a new database using `sql/phpbugsdb_create.sql` and insert fixtures using
        +`sql/bugs.sql`.
        
        From e6700f9eeddc1b1a7c0573aa48e0082eb650881b Mon Sep 17 00:00:00 2001
        From: Peter Kokot 
        Date: Mon, 22 Oct 2018 11:45:22 +0200
        Subject: [PATCH 105/277] Change visitor_ip MySQL field type to varbinary(16)
        
        This now enables local installation more easily without manually
        adjusting the field. The new varbinary field has been recently updated
        in the code for reporting bugs and adding comments.
        ---
         sql/bugs.sql | 4 ++--
         1 file changed, 2 insertions(+), 2 deletions(-)
        
        diff --git a/sql/bugs.sql b/sql/bugs.sql
        index e7ad186d..11801253 100644
        --- a/sql/bugs.sql
        +++ b/sql/bugs.sql
        @@ -21,7 +21,7 @@ CREATE TABLE bugdb (
           block_user_comment char(1) default 'N',
           cve_id varchar(15) default NULL,
           private char(1) default 'N',
        -  visitor_ip int(10) unsigned NOT NULL,
        +  visitor_ip varbinary(16) NOT NULL,
           PRIMARY KEY (id),
           KEY php_version (php_version(1)),
           KEY status (status),
        @@ -37,7 +37,7 @@ CREATE TABLE bugdb_comments (
           ts datetime NOT NULL default CURRENT_TIMESTAMP,
           comment text NOT NULL,
           comment_type varchar(10) default 'comment',
        -  visitor_ip int(8) UNSIGNED NOT NULL,
        +  visitor_ip varbinary(16) NOT NULL,
           PRIMARY KEY  (id),
           KEY bug (bug,id,ts),
           FULLTEXT KEY comment (comment)
        
        From c2a40c6bf0a7ecf4eab0ff4f21d8abe6d32d1bfc Mon Sep 17 00:00:00 2001
        From: Peter Kokot 
        Date: Tue, 23 Oct 2018 00:40:44 +0200
        Subject: [PATCH 106/277] Replace PEAR MDB2 constants with PDO constants
        
        The changed constants are more understandable using PDO constants
        nor are the dedicated definition needed anymore.
        ---
         include/classes/bug_ghpulltracker.php |  2 +-
         include/classes/bug_patchtracker.php  | 10 +++++-----
         include/classes/bug_pdo.php           |  5 -----
         include/functions.php                 | 18 +++++++++---------
         www/admin/index.php                   | 10 +++++-----
         www/api.php                           |  2 +-
         www/bug-pwd-finder.php                |  2 +-
         www/lstats.php                        |  2 +-
         www/stats.php                         |  4 ++--
         9 files changed, 25 insertions(+), 30 deletions(-)
        
        diff --git a/include/classes/bug_ghpulltracker.php b/include/classes/bug_ghpulltracker.php
        index 6bc0ad28..47d1524b 100644
        --- a/include/classes/bug_ghpulltracker.php
        +++ b/include/classes/bug_ghpulltracker.php
        @@ -72,6 +72,6 @@ function listPulls($bugid)
         			ORDER BY github_repo, github_pull_id DESC
         		';
         
        -		return $this->_dbh->prepare($query)->execute(array($bugid))->fetchAll(MDB2_FETCHMODE_ASSOC);
        +		return $this->_dbh->prepare($query)->execute(array($bugid))->fetchAll(PDO::FETCH_ASSOC);
         	}
         }
        diff --git a/include/classes/bug_patchtracker.php b/include/classes/bug_patchtracker.php
        index 39a79d57..2324ecaf 100644
        --- a/include/classes/bug_patchtracker.php
        +++ b/include/classes/bug_patchtracker.php
        @@ -285,7 +285,7 @@ function listPatches($bugid)
         			ORDER BY revision DESC
         		';
         
        -		return $this->_dbh->prepare($query)->execute(array($bugid))->fetchAll(MDB2_FETCHMODE_ORDERED, true, false, true);
        +		return $this->_dbh->prepare($query)->execute(array($bugid))->fetchAll(PDO::FETCH_NUM, true, false, true);
         	}
         
         	/**
        @@ -302,7 +302,7 @@ function listRevisions($bugid, $patch)
         			WHERE bugdb_id = ? AND patch = ?
         			ORDER BY revision DESC
         		';
        -		return $this->_dbh->prepare($query)->execute(array($bugid, $patch))->fetchAll(MDB2_FETCHMODE_ORDERED);
        +		return $this->_dbh->prepare($query)->execute(array($bugid, $patch))->fetchAll(PDO::FETCH_NUM);
         	}
         
         	/**
        @@ -326,7 +326,7 @@ function getDeveloper($bugid, $patch, $revision = false)
         			SELECT developer, revision
         			FROM bugdb_patchtracker
         			WHERE bugdb_id = ? AND patch = ? ORDER BY revision DESC
        -		')->execute(array($bugid, $patch))->fetchAll(MDB2_FETCHMODE_ASSOC);
        +		')->execute(array($bugid, $patch))->fetchAll(PDO::FETCH_ASSOC);
         	}
         
         	function getObsoletingPatches($bugid, $patch, $revision)
        @@ -335,7 +335,7 @@ function getObsoletingPatches($bugid, $patch, $revision)
         			SELECT bugdb_id, patch, revision
         			FROM bugdb_obsoletes_patches
         			WHERE	bugdb_id = ? AND obsolete_patch = ? AND obsolete_revision = ?
        -		')->execute(array($bugid, $patch, $revision))->fetchAll(MDB2_FETCHMODE_ASSOC);
        +		')->execute(array($bugid, $patch, $revision))->fetchAll(PDO::FETCH_ASSOC);
         	}
         
         	function getObsoletePatches($bugid, $patch, $revision)
        @@ -344,7 +344,7 @@ function getObsoletePatches($bugid, $patch, $revision)
         			SELECT bugdb_id, obsolete_patch, obsolete_revision
         			FROM bugdb_obsoletes_patches
         			WHERE bugdb_id = ? AND patch = ? AND revision = ?
        -		')->execute(array($bugid, $patch, $revision))->fetchAll(MDB2_FETCHMODE_ASSOC);
        +		')->execute(array($bugid, $patch, $revision))->fetchAll(PDO::FETCH_ASSOC);
         	}
         
         	/**
        diff --git a/include/classes/bug_pdo.php b/include/classes/bug_pdo.php
        index f5e8efa9..237163ff 100644
        --- a/include/classes/bug_pdo.php
        +++ b/include/classes/bug_pdo.php
        @@ -9,11 +9,6 @@
          * @author Maciej Sobaczewski 
          */
         
        -// Define missing constants
        -define('MDB2_FETCHMODE_ASSOC', PDO::FETCH_ASSOC);
        -define('MDB2_FETCHMODE_ORDERED', PDO::FETCH_NUM);
        -define('MDB2_PREPARE_MANIP', null);
        -
         class Bug_PDO extends PDO
         {
             /**
        diff --git a/include/functions.php b/include/functions.php
        index 31e5bf40..3216978d 100644
        --- a/include/functions.php
        +++ b/include/functions.php
        @@ -215,7 +215,7 @@ function get_pseudo_packages($project, $return_disabled = true)
         		$where.= " AND disabled = 0";
         	}
         
        -	$data = $dbh->queryAll("SELECT * FROM bugdb_pseudo_packages WHERE $where ORDER BY parent, disabled, id", null, MDB2_FETCHMODE_ASSOC);
        +	$data = $dbh->queryAll("SELECT * FROM bugdb_pseudo_packages WHERE $where ORDER BY parent, disabled, id", null, PDO::FETCH_ASSOC);
         
         	// Convert flat array to nested strucutre
         	foreach ($data as &$node)
        @@ -1181,13 +1181,13 @@ function get_old_comments($bug_id, $all = 0)
         
         	// skip the most recent unless the caller wanted all comments
         	if (!$all) {
        -		$row = $res->fetchRow(MDB2_FETCHMODE_ORDERED);
        +		$row = $res->fetchRow(PDO::FETCH_NUM);
         		if (!$row) {
         			return '';
         		}
         	}
         
        -	while (($row = $res->fetchRow(MDB2_FETCHMODE_ORDERED)) && strlen($output) < $max_message_length && $count++ < $max_comments) {
        +	while (($row = $res->fetchRow(PDO::FETCH_NUM)) && strlen($output) < $max_message_length && $count++ < $max_comments) {
         		$email = spam_protect($row[1], 'text');
         		$output .= "[{$row[0]}] {$email}\n\n{$row[2]}\n\n{$divider}\n";
         	}
        @@ -1197,7 +1197,7 @@ function get_old_comments($bug_id, $all = 0)
         		if (!$res) {
         			return $output;
         		}
        -		$row = $res->fetchRow(MDB2_FETCHMODE_ORDERED);
        +		$row = $res->fetchRow(PDO::FETCH_NUM);
         		if (!$row) {
         			return $output;
         		}
        @@ -1477,7 +1477,7 @@ function unsubscribe_hash($bug_id, $email)
         		WHERE bug_id = ? AND email = ?
         	";
         
        -	$affected = $dbh->prepare($query, null, MDB2_PREPARE_MANIP)->execute([$hash, $bug_id, $email]);
        +	$affected = $dbh->prepare($query, null, null)->execute([$hash, $bug_id, $email]);
         
         	if ($affected > 0) {
         		$hash = urlencode($hash);
        @@ -1530,7 +1530,7 @@ function unsubscribe($bug_id, $hash)
         		WHERE bug_id = ? AND unsubscribe_hash = ? LIMIT 1
         	";
         
        -	$sub = $dbh->prepare($query)->execute([$bug_id, $hash])->fetch(MDB2_FETCHMODE_ASSOC);
        +	$sub = $dbh->prepare($query)->execute([$bug_id, $hash])->fetch(PDO::FETCH_ASSOC);
         
         	if (!$sub) {
         		return false;
        @@ -1573,7 +1573,7 @@ function get_resolve_reasons($project = false)
         	if (!$res) {
         		throw new Exception("SQL Error in get_resolve_reasons");
         	}
        -	while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)) {
        +	while ($row = $res->fetchRow(PDO::FETCH_ASSOC)) {
         		if (!empty($row['package_name'])) {
         			$variations[$row['name']][$row['package_name']] = $row['message'];
         		} else {
        @@ -1608,7 +1608,7 @@ function bugs_get_bug($bug_id)
         		WHERE b.id = ?
         		GROUP BY bug';
         
        -	return $dbh->prepare($query)->execute([$bug_id])->fetchRow(MDB2_FETCHMODE_ASSOC);
        +	return $dbh->prepare($query)->execute([$bug_id])->fetchRow(PDO::FETCH_ASSOC);
         }
         
         /**
        @@ -1628,7 +1628,7 @@ function bugs_get_bug_comments($bug_id)
         		WHERE c.bug = ?
         		GROUP BY c.id ORDER BY c.ts
         	";
        -	return $dbh->prepare($query)->execute([$bug_id])->fetchAll(MDB2_FETCHMODE_ASSOC);
        +	return $dbh->prepare($query)->execute([$bug_id])->fetchAll(PDO::FETCH_ASSOC);
         }
         
         /**
        diff --git a/www/admin/index.php b/www/admin/index.php
        index 3b047b95..5d60eb00 100644
        --- a/www/admin/index.php
        +++ b/www/admin/index.php
        @@ -59,7 +59,7 @@
         	");
         
         	echo "
        \n"; - while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)) { + while ($row = $res->fetchRow(PDO::FETCH_ASSOC)) { echo "
        ", $row['name'], ":
        \n
        ", mailto_list(explode(',', $row['list_email'])), "
        \n"; } echo "
        \n"; @@ -73,7 +73,7 @@ echo "

        List Responses

        \n"; $rows = array(); - while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)) { + while ($row = $res->fetchRow(PDO::FETCH_ASSOC)) { // This is ugly but works (tm) $row['message'] = nl2br($row['message']); @@ -86,13 +86,13 @@ $sql = "SELECT version() mysql_version\n"; - while ($row = $res->fetchRow(MDB2_FETCHMODE_ORDERED)) { + while ($row = $res->fetchRow(PDO::FETCH_NUM)) { $table = $row[0]; $sql .= "\t, (SELECT COUNT(*) FROM `$table`) `cnt_$table`\n"; } $res = $dbh->query($sql); - $row = $res->fetchRow(MDB2_FETCHMODE_ASSOC); + $row = $res->fetchRow(PDO::FETCH_ASSOC); echo "

        Running MySQL ".$row['mysql_version']."

        "; unset($row['mysql_version']); @@ -110,7 +110,7 @@ $rows = array(); $res = $dbh->query("SHOW TABLE STATUS"); echo "

        Table status:

        \n"; - while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)) { + while ($row = $res->fetchRow(PDO::FETCH_ASSOC)) { $rows[] = $row; } diff --git a/www/api.php b/www/api.php index 53228ff6..b1e2e17a 100644 --- a/www/api.php +++ b/www/api.php @@ -27,7 +27,7 @@ "; //@todo add error handling - $rows = $dbh->prepare($query)->execute(array())->fetchAll(MDB2_FETCHMODE_ASSOC); + $rows = $dbh->prepare($query)->execute(array())->fetchAll(PDO::FETCH_ASSOC); if (!$rows) { echo 'The fail train has arrived.'; exit; diff --git a/www/bug-pwd-finder.php b/www/bug-pwd-finder.php index f7a6ac66..eb78c543 100644 --- a/www/bug-pwd-finder.php +++ b/www/bug-pwd-finder.php @@ -28,7 +28,7 @@ $query = "SELECT email, passwd FROM bugdb WHERE id = '{$bug_id}'"; // Run the query - $row = $dbh->prepare($query)->execute()->fetchRow(MDB2_FETCHMODE_ASSOC); + $row = $dbh->prepare($query)->execute()->fetchRow(PDO::FETCH_ASSOC); if (is_null($row)) { $errors[] = "Invalid bug id provided: #{$bug_id}"; diff --git a/www/lstats.php b/www/lstats.php index d8a8ae2a..cf705bc2 100644 --- a/www/lstats.php +++ b/www/lstats.php @@ -28,7 +28,7 @@ function get_status_count ($status, $category = '') $query.= "AND bug_type NOT IN({$excluded})"; $res = $dbh->prepare($query)->execute(array()); - $row = $res->fetchRow(MDB2_FETCHMODE_ORDERED); + $row = $res->fetchRow(PDO::FETCH_NUM); return $row[0]; } diff --git a/www/stats.php b/www/stats.php index ec9cb2ae..cc460379 100644 --- a/www/stats.php +++ b/www/stats.php @@ -59,7 +59,7 @@ $result = $dbh->prepare($query)->execute(); -while ($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) { +while ($row = $result->fetchRow(PDO::FETCH_ASSOC)) { $pkg_tmp[$row['status']][$row['package_name']] = $row['quant']; @$pkg_total[$row['package_name']] += $row['quant']; @$all[$row['status']] += $row['quant']; @@ -161,7 +161,7 @@ $result = $dbh->prepare($query)->execute(); $last_date = null; -while ($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) { +while ($row = $result->fetchRow(PDO::FETCH_ASSOC)) { if ($row['d'] != $last_date) { if ($last_date !== null) { echo "
        \n\n"; From 5344b77166b0826165792f12042a72383fcbba93 Mon Sep 17 00:00:00 2001 From: zrhoffman Date: Sun, 7 Oct 2018 14:48:20 -0500 Subject: [PATCH 107/277] Fix #76273: Package affected section should be split - include package-affected.js on bug reporting page - package-affected.js: a dropdown for package categories - package-affected.js: change the package list when the user selects a different package group - packages affected: change select size to 5 so the user can see when the the package list changes --- www/js/package-affected.js | 104 +++++++++++++++++++++++++++++++++++++ www/report.php | 16 ++++-- 2 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 www/js/package-affected.js diff --git a/www/js/package-affected.js b/www/js/package-affected.js new file mode 100644 index 00000000..1d44fa8c --- /dev/null +++ b/www/js/package-affected.js @@ -0,0 +1,104 @@ +'use strict'; + +window.addEventListener( + 'load', + function () { + document + .querySelectorAll('select[name="in[package_name]"]') + .forEach( + function (select) { + var packageGroup = document.createElement('select'), + initialValue = select.value, + initialGroup = select.querySelector(':scope option[value="' + initialValue + '"]').parentNode; + + packageGroup.name = 'in[package_group]'; + + select + .querySelectorAll(':scope optgroup') + .forEach( + function (optgroup) { + var packageOption = document.createElement('option'), + groupName = optgroup.label; + + packageOption.textContent = groupName; + packageOption.value = groupName; + packageGroup.appendChild(packageOption); + + optgroup + .querySelectorAll(':scope option') + .forEach( + function (option) { + option.setAttribute('bug-group', groupName); + } + ); + } + ); + + var instructions = select.querySelector(':scope option[value="none"]'); + if (instructions instanceof HTMLElement) { + instructions.textContent = 'Select a category'; + } + + select + .querySelectorAll(':scope optgroup') + .forEach( + function (optgroup) { + optgroup.style.display = 'none'; + } + ); + + function updateGroup() { + select.disabled = false; + + if (instructions instanceof HTMLElement) { + instructions.style.display = 'none'; + } + + var previousOptions = select.querySelectorAll(':scope > option:not([value=none])'), + nextLabel = packageGroup.value, + nextGroup = select.querySelector(':scope optgroup[label="' + nextLabel + '"]'); + + if (previousOptions.length !== 0) { + var previousLabel = previousOptions[0].getAttribute('bug-group'), + previousGroup = select.querySelector(':scope optgroup[label="' + previousLabel + '"]'); + + moveOptions(select, previousGroup); + } + + moveOptions(nextGroup, select); + } + + function moveOptions(from, to) { + from.querySelectorAll(':scope > option') + .forEach( + function (option) { + to.appendChild(option); + } + ); + } + + packageGroup.addEventListener('change', updateGroup); + + if (initialGroup instanceof HTMLOptGroupElement) { + packageGroup.value = initialGroup.label; + moveOptions(initialGroup, select); + } else { + select.disabled = true; + select.value = null; + } + + packageGroup.style.marginRight = '.5em'; + [select, packageGroup].forEach( + function (element) { + element.size = 5; + element.style.width = '22em'; + } + ); + + select + .parentNode + .insertBefore(packageGroup, select); + } + ); + } +); diff --git a/www/report.php b/www/report.php index 1b9606e3..5151ffe8 100644 --- a/www/report.php +++ b/www/report.php @@ -27,6 +27,10 @@ $numeralCaptcha = new Text_CAPTCHA_Numeral(); } +$packageAffectedScript = << +SCRIPT; + // Handle input if (isset($_POST['in'])) { @@ -81,7 +85,7 @@ if (count($possible_duplicates) == 0) { $ok_to_submit_report = true; } else { - response_header("Report - Confirm"); + response_header("Report - Confirm", $packageAffectedScript); if (count($_FILES)) { echo '

        WARNING: YOU MUST RE-UPLOAD YOUR PATCH, OR IT WILL BE IGNORED

        '; } @@ -150,7 +154,7 @@ if (isset($_POST['edit_after_preview'])) { $ok_to_submit_report = false; - response_header("Report - New"); + response_header("Report - New", $packageAffectedScript); } if ($ok_to_submit_report) { @@ -317,14 +321,14 @@ } } else { // had errors... - response_header('Report - Problems'); + response_header('Report - Problems', $packageAffectedScript); } } // end of if input $package = !empty($_REQUEST['package']) ? $_REQUEST['package'] : (!empty($package_name) ? $package_name : (isset($_POST['in']) && $_POST['in'] && isset($_POST['in']['package_name']) ? $_POST['in']['package_name'] : '')); if (!is_string($package)) { - response_header('Report - Problems'); + response_header('Report - Problems', $packageAffectedScript); $errors[] = 'Invalid package name passed. Please fix it and try again.'; display_bug_error($errors); response_footer(); @@ -346,7 +350,9 @@ 'php_os' => '', 'passwd' => '', ); - response_header('Report - New'); + + + response_header('Report - New', $packageAffectedScript); ?>

        From 4bdb303453c1c44fa770483d10e4f62ebb8ca881 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 23 Oct 2018 03:05:34 +0200 Subject: [PATCH 108/277] Refactor PDO wrapper to not depend on MDB2 anymore The Bug_PDO is a thin PDO wrapper not dependant on the MDB2 anymore. The rpc.php page included some old MDB2::errorMessage() usage which is now refactored differently using only PDO way and current errors handling. --- include/classes/bug_pdo.php | 16 ++++++---------- www/rpc.php | 12 ++++++------ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/include/classes/bug_pdo.php b/include/classes/bug_pdo.php index 237163ff..536762ce 100644 --- a/include/classes/bug_pdo.php +++ b/include/classes/bug_pdo.php @@ -1,10 +1,6 @@ */ @@ -23,8 +19,8 @@ public function __construct($dsn, $username = '', $password = '', array $options } /** - * MDB2::espace() doesn't put apostrophes around the text and PDO does so we - * need to strip outermost characters. + * PDO puts apostrophes around the text so we need to strip the outermost + * characters. */ public function escape($text, $escape_wildcards = false) { @@ -40,10 +36,10 @@ public function queryAll($query, $types = null, $fetchmode = null, $rekey = fals class Bug_PDOStatement extends PDOStatement { /** - * MDB2 allows for chaining execute() method like so: + * This allows chaining execute() method: * $db->query('SELECT a FROM b WHERE c = ?')->execute('foo')->fetch(); - * PDOStatement::execute(), on the other hand, returns boolean. Change it - * to return $this and thus allow futher method chaining. + * PDOStatement::execute(), on the other hand, returns boolean. Change it to + * return $this and thus allow further method chaining. */ public function execute($input_parameters = NULL) { diff --git a/www/rpc.php b/www/rpc.php index 0a97a497..5aa60e1d 100644 --- a/www/rpc.php +++ b/www/rpc.php @@ -49,10 +49,10 @@ $ncomment = htmlspecialchars(trim($_POST['ncomment'])); $from = "{$user}@php.net"; - /* svn log comment */ - $res = bugs_add_comment($bug_id, $from, $user, $ncomment, 'svn'); + try { + /* svn log comment */ + bugs_add_comment($bug_id, $from, $user, $ncomment, 'svn'); - if ($res) { /* Close the bug report as requested if it is not already closed */ if (!empty($_POST['status']) && $bug['status'] !== 'Closed' @@ -69,7 +69,7 @@ $log_comment = bug_diff_render_html($changed); if (!empty($log_comment)) { /* Add a log of status change */ - $res = bugs_add_comment($bug_id, $from, '', $log_comment, 'log'); + bugs_add_comment($bug_id, $from, '', $log_comment, 'log'); } } @@ -79,8 +79,8 @@ echo json_encode(array('result' => array('status' => $bug))); exit; - } else { - echo json_encode(array('result' => array('error' => MDB2::errorMessage($res)))); + } catch (Exception $e) { + echo json_encode(array('result' => array('error' => $e->getMessage()))); exit; } } else if (!empty($_POST['getbug'])) { From a0b80a831bc7a68c201f42d2f186a9e316029b0a Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 23 Oct 2018 03:21:47 +0200 Subject: [PATCH 109/277] Add note about rpc.php --- www/rpc.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/www/rpc.php b/www/rpc.php index 5aa60e1d..e63b93c0 100644 --- a/www/rpc.php +++ b/www/rpc.php @@ -1,5 +1,10 @@ Date: Tue, 23 Oct 2018 03:31:22 +0200 Subject: [PATCH 110/277] Fix permissions Having synced file permissions in Git repositories is a reccurring issue. Git can track files as executables (0755) or not (0644). Usually, all files except the executable ones such as command line scripts or binary executables, should be set to 0644. This patch syncs permissions in the Git repository. --- www/images/box-0.gif | Bin www/images/box-1.gif | Bin www/images/favicon.ico | Bin www/search-howto.php | 0 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 www/images/box-0.gif mode change 100755 => 100644 www/images/box-1.gif mode change 100755 => 100644 www/images/favicon.ico mode change 100755 => 100644 www/search-howto.php diff --git a/www/images/box-0.gif b/www/images/box-0.gif old mode 100755 new mode 100644 diff --git a/www/images/box-1.gif b/www/images/box-1.gif old mode 100755 new mode 100644 diff --git a/www/images/favicon.ico b/www/images/favicon.ico old mode 100755 new mode 100644 diff --git a/www/search-howto.php b/www/search-howto.php old mode 100755 new mode 100644 From 8fe6914127b3b3da776a67c25db29ae8e0a5b16c Mon Sep 17 00:00:00 2001 From: zrhoffman Date: Tue, 23 Oct 2018 04:30:49 +0200 Subject: [PATCH 111/277] Fix 76079: Attaching PRs to bug issues broken This patch fixes attaching pull requests to bug tickets. This will need to be resolved also using different consuming of the GitHub API in the near future due to possible rate limits and other limitations. Additionally, would be good to get the PEAR dependency refactored for handling the errors to not depend on outdated PEAR. Otherwise, this fixes bug #76079 thanks to @zrhoffman --- www/gh-pull-add.php | 1 + 1 file changed, 1 insertion(+) diff --git a/www/gh-pull-add.php b/www/gh-pull-add.php index 8a32472b..b1fe0682 100644 --- a/www/gh-pull-add.php +++ b/www/gh-pull-add.php @@ -2,6 +2,7 @@ // Obtain common includes require_once '../include/prepend.php'; +require_once 'PEAR.php'; session_start(); $canpatch = true; From 3b0b87e230f278e0db9b04fbbff05a8a7dd55cc6 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 23 Oct 2018 05:39:49 +0200 Subject: [PATCH 112/277] Fix #76961: bugs.php.net/vote.php throws a Server Error --- www/vote.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/www/vote.php b/www/vote.php index 358ee995..0c4b311b 100644 --- a/www/vote.php +++ b/www/vote.php @@ -87,10 +87,10 @@ function get_real_ip () WHERE bug = ? AND ip = ?") ->execute(array( $score, - ($reproduced == 1 ? "1," : "0,"), - ($reproduced != 2 ? "1," : "0,"), - ($reproduced ? "$sameos," : "NULL,"), - ($reproduced ? "$samever" : "NULL"), + ($reproduced == 1 ? "1" : "0"), + ($reproduced != 2 ? "1" : "0"), + ($reproduced ? "$sameos" : null), + ($reproduced ? "$samever" : null), $id, $ip )); From ddd17b47b0bdefb5bb0d2b5f24fb4bc7704a9896 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 23 Oct 2018 15:17:41 +0200 Subject: [PATCH 113/277] Update link to MySQL Full Text search docs --- www/search-howto.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/search-howto.php b/www/search-howto.php index 30a9fabb..10efdfb8 100644 --- a/www/search-howto.php +++ b/www/search-howto.php @@ -51,7 +51,7 @@

      • any : One or more (any) of the search terms may be present.
      • raw : Allows full use of MySQL's - FULLTEXT + FULLTEXT boolean search operators.
      From 21e0f760f1375486398655266263b3a2a9dcf6be Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 23 Oct 2018 17:02:10 +0200 Subject: [PATCH 114/277] Change dirname(__FILE__) to shorter __DIR__ --- include/prepend.php | 2 +- scripts/cron/no-feedback | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/prepend.php b/include/prepend.php index ba285e44..2e67f401 100644 --- a/include/prepend.php +++ b/include/prepend.php @@ -4,7 +4,7 @@ $site = 'php'; $siteBig = 'PHP'; -$ROOT_DIR = realpath(dirname(__FILE__) . '/../'); +$ROOT_DIR = realpath(__DIR__ . '/../'); $local_cfg = "{$ROOT_DIR}/local_config.php"; if (file_exists($local_cfg)) { diff --git a/scripts/cron/no-feedback b/scripts/cron/no-feedback index cc658c96..a6d263b2 100755 --- a/scripts/cron/no-feedback +++ b/scripts/cron/no-feedback @@ -3,7 +3,7 @@ # this script closes bugs due to lack of feedback. -require dirname(__FILE__).'/../../include/prepend.php'; +require __DIR__.'/../../include/prepend.php'; # date interval to close after $after = "7 DAY"; From 8c1bedf1c0a8046a18fa68d59935924b67824895 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 23 Oct 2018 16:31:50 +0200 Subject: [PATCH 115/277] Update http to https Changes: - http links updated to https (those that work so far) - us3.php.net mirror changed to php.net for fetching PHP versions - some outdated links refreshed --- include/functions.php | 10 +++++----- include/php_versions.php | 8 ++++---- scripts/cron/email-assigned | 4 ++-- templates/addghpull.php | 2 +- www/bug.php | 4 ++-- www/bugs-generating-backtrace-win32.php | 10 +++++----- www/css/style.css | 2 +- www/how-to-report.php | 8 ++++---- www/index.php | 2 +- www/js/Markdown.Converter.js | 14 +++++++------- www/report.php | 6 +++--- 11 files changed, 35 insertions(+), 35 deletions(-) diff --git a/include/functions.php b/include/functions.php index 3216978d..101e8d97 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1741,9 +1741,9 @@ function response_header($title, $extraHeaders = '') - php.net |  - support |  - documentation |  + php.net |  + support |  + documentation |  report a bug |  advanced search |  search howto |  @@ -1805,8 +1805,8 @@ function response_footer($extra_html = '') - PHP - Copyright © 2001- The PHP Group
      + PHP + Copyright © 2001- The PHP Group
      All rights reserved.
      diff --git a/include/php_versions.php b/include/php_versions.php index 20529c50..46ebc75e 100644 --- a/include/php_versions.php +++ b/include/php_versions.php @@ -1,14 +1,14 @@ /tmp/versions.php the versions are weighted by the following: - major+minor version desc (7>5.4>5.3>master) - between a minor version we order by the micro if available: first the qa releases: alpha/beta/rc, then the stable, then the Git versions(snaps, Git) - Stable releases are pulled from http://php.net/releases/active.php + Stable releases are pulled from https://php.net/releases/active.php */ // Custom versions appended to the list @@ -29,7 +29,7 @@ $versions = array_merge($versions, $custom_versions); function buildVersions() { - $dev_versions = json_decode(file_get_contents('http://qa.php.net/api.php?type=qa-releases&format=json&only=dev_versions')); + $dev_versions = json_decode(file_get_contents('https://qa.php.net/api.php?type=qa-releases&format=json&only=dev_versions')); $versions = array(); @@ -55,7 +55,7 @@ function buildVersions() { } } - $stable_releases = json_decode(file_get_contents('http://us3.php.net/releases/active.php'), true); + $stable_releases = json_decode(file_get_contents('https://php.net/releases/active.php'), true); foreach ($stable_releases as $major => $major_releases) { foreach ($major_releases as $release) { $version_parts = parseVersion($release['version']); diff --git a/scripts/cron/email-assigned b/scripts/cron/email-assigned index 3a654cf6..6257c521 100755 --- a/scripts/cron/email-assigned +++ b/scripts/cron/email-assigned @@ -6,7 +6,7 @@ require __DIR__ . '/../../include/prepend.php'; /* Many times bugs are assigned to humans but not given the 'Assigned' status. Our bug tracker is a little odd that way. - 'No Feedback' was once a part of this, but no longer: http://news.php.net/php.webmaster/8828 + 'No Feedback' was once a part of this, but no longer: https://news.php.net/php.webmaster/8828 */ $sql = "SELECT id, package_name, bug_type, sdesc, status, assign, UNIX_TIMESTAMP(ts1) AS ts_opened, UNIX_TIMESTAMP(ts2) AS ts_changed FROM `bugdb` @@ -49,7 +49,7 @@ function format_email_body($binfos) { $links .= " Title: $link_title\r\n"; $links .= " Type: {$binfo['bug_type']} with Status: {$binfo['status']}\r\n"; - $links .= " Link: http://bugs.php.net/{$binfo['id']}\r\n\r\n"; + $links .= " Link: https://bugs.php.net/{$binfo['id']}\r\n\r\n"; } $assigned = $binfo['assign']; diff --git a/templates/addghpull.php b/templates/addghpull.php index 22d3d5b8..ea9dc264 100644 --- a/templates/addghpull.php +++ b/templates/addghpull.php @@ -45,7 +45,7 @@ - The repository must belong to http://github.com/php/. + The repository must belong to https://github.com/php diff --git a/www/bug.php b/www/bug.php index 79be771b..84d8d539 100644 --- a/www/bug.php +++ b/www/bug.php @@ -665,7 +665,7 @@ Status: Package: - ', htmlspecialchars($bug['package_name']); ?>PECL)' : ''; ?> + ', htmlspecialchars($bug['package_name']); ?>PECL)' : ''; ?> @@ -679,7 +679,7 @@ Private report: CVE-ID: - %1$s', htmlspecialchars($bug['cve_id'])); } else { ?>None + %1$s', htmlspecialchars($bug['cve_id'])); } else { ?>None diff --git a/www/bugs-generating-backtrace-win32.php b/www/bugs-generating-backtrace-win32.php index ee15f67e..9b5608dc 100644 --- a/www/bugs-generating-backtrace-win32.php +++ b/www/bugs-generating-backtrace-win32.php @@ -16,7 +16,7 @@

      You'll need to install MS Visual Studio 2008, 2012 or later. You'll also need to

      @@ -45,14 +45,14 @@

    Generating backtrace, without compiler, on Windows

    You'll need:

    diff --git a/www/css/style.css b/www/css/style.css index c9dcbe9e..2efd09d8 100644 --- a/www/css/style.css +++ b/www/css/style.css @@ -784,7 +784,7 @@ td.search-next { background-color: #B5C7CD; } -/* {{{ Stolen from http://www.devbridge.com/projects/autocomplete/jquery/ */ +/* {{{ Stolen from https://www.devbridge.com/sourcery/components/jquery-autocomplete/ */ .autocomplete-w1 { background: url(http://github.com/images/shadow.png) no-repeat bottom right; position: absolute; diff --git a/www/how-to-report.php b/www/how-to-report.php index 68bf6ed8..7d1769b1 100644 --- a/www/how-to-report.php +++ b/www/how-to-report.php @@ -23,7 +23,7 @@

    Take special note of that word in bold above. The people who are going to help you with a bug you report are volunteers. Not only are you not paying them to help you, but nobody else is either. So, to paraphrase the -immortal words of Bill and Ted, +immortal words of Bill and Ted, "be excellent to them".

    Beyond that golden rule, what follows are some additional tips on ways to @@ -58,7 +58,7 @@

    If you don't understand an error message, ask for help.

    Don't report an error message you don't understand as a bug. There are a lot of places you can ask for help +href="https://php.net/support.php">a lot of places you can ask for help in understanding what is going on before you can claim that an error message you do not understand is a bug.

    @@ -101,7 +101,7 @@ bug in a language other than English, many (if not most) of the people who would otherwise help you won't be able to. If you're worried about your English skills making it difficult to describe the bug, you might try asking for help -on one of the non-English +on one of the non-English mailing lists.

    Don't report bugs about old versions.

    @@ -131,7 +131,7 @@ writing guidelines
  • - Simon Tatham's How + Simon Tatham's How to Report Bugs Effectively
  • diff --git a/www/index.php b/www/index.php index bd91baa8..58d78524 100644 --- a/www/index.php +++ b/www/index.php @@ -67,7 +67,7 @@
  • Make sure it isn't a support question. For support, - see the support page. + see the support page.
  • diff --git a/www/js/Markdown.Converter.js b/www/js/Markdown.Converter.js index cfec79ef..d9c549e6 100644 --- a/www/js/Markdown.Converter.js +++ b/www/js/Markdown.Converter.js @@ -88,7 +88,7 @@ else // caused an exception (and hence stopped the rendering) when the user entered // e.g. [push] or [__proto__]. Adding a prefix to the actual key prevents this // (since no builtin property starts with "s_"). See - // http://meta.stackoverflow.com/questions/64655/strange-wmd-bug + // https://meta.stackexchange.com/questions/64655/strange-wmd-bug // (granted, switching from Array() to Object() alone would have left only __proto__ // to be a problem) function SaveHash() { } @@ -331,7 +331,7 @@ else [ ]{0,3} // attacklab: g_tab_width - 1 ( // save in $1 -]|-[^>])(?:[^-]|-[^-])*)--) // see http://www.w3.org/TR/html-markup/syntax.html#comments and http://meta.stackoverflow.com/q/95256 + (--(?:|(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--) // see https://www.w3.org/TR/html/syntax.html#comments and https://meta.stackoverflow.com/q/95256 > [ \t]* (?=\n{2,}) // followed by a blank line @@ -421,7 +421,7 @@ else text = _DoImages(text); text = _DoAnchors(text); - // Make links out of things like `` + // Make links out of things like `` // Must come after _DoAnchors(), because you can use < and > // delimiters in inline links like [this](). text = _DoAutoLinks(text); @@ -452,7 +452,7 @@ else text = text.replace(regex, function (wholeMatch) { var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, "$1`"); - tag = escapeCharacters(tag, wholeMatch.charAt(1) == "!" ? "\\`*_/" : "\\`*_"); // also escape slashes in comments to prevent autolinking there -- http://meta.stackoverflow.com/questions/95987 + tag = escapeCharacters(tag, wholeMatch.charAt(1) == "!" ? "\\`*_/" : "\\`*_"); // also escape slashes in comments to prevent autolinking there -- https://meta.stackoverflow.com/questions/95987 return tag; }); @@ -760,7 +760,7 @@ else // // attacklab: add sentinel to hack around khtml/safari bug: - // http://bugs.webkit.org/show_bug.cgi?id=11231 + // https://bugs.webkit.org/show_bug.cgi?id=11231 text += "~0"; // Re-usable pattern to match any entirel ul or ol list: @@ -1198,13 +1198,13 @@ else function _DoAutoLinks(text) { // note that at this point, all other URL in the text are already hyperlinked as - // *except* for the case + // *except* for the case // automatically add < and > around unadorned raw hyperlinks // must be preceded by space/BOF and followed by non-word/EOF character text = text.replace(/(^|\s)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]])($|\W)/gi, "$1<$2$3>$4"); - // autolink anything like + // autolink anything like var replacer = function (wholematch, m1) { return "" + pluginHooks.plainLinkText(m1) + ""; } text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer); diff --git a/www/report.php b/www/report.php index 5151ffe8..83f56738 100644 --- a/www/report.php +++ b/www/report.php @@ -342,7 +342,7 @@ 'bug_type' => isset($_GET['bug_type']) ? clean($_GET['bug_type']) : '', 'email' => '', 'sdesc' => '', - 'ldesc' => isset($_GET['manpage']) ? clean("\n---\nFrom manual page: http://www.php.net/" . ltrim($_GET['manpage'], '/') . "\n---\n") : '', + 'ldesc' => isset($_GET['manpage']) ? clean("\n---\nFrom manual page: https://php.net/" . ltrim($_GET['manpage'], '/') . "\n---\n") : '', 'repcode' => '', 'expres' => '', 'actres' => '', @@ -362,14 +362,14 @@

    If you aren't sure that what you're about to report is a bug, you should ask for help using one of the means for support - listed here. + listed here.

    Failure to follow these instructions may result in your bug simply being marked as "not a bug."

    -

    Report PEAR related bugs here

    +

    Report PEAR related bugs here

    If you feel this bug concerns a security issue, e.g. a buffer overflow, weak encryption, etc, then email From f1614132bc4d35ead01136c4a91ddf728285993f Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 23 Oct 2018 19:36:59 +0200 Subject: [PATCH 116/277] Use SQL standard comment instead of MySQL specific --- sql/bugs.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/bugs.sql b/sql/bugs.sql index 11801253..12f0b887 100644 --- a/sql/bugs.sql +++ b/sql/bugs.sql @@ -115,6 +115,6 @@ CREATE TABLE bugdb_pulls ( PRIMARY KEY (bugdb_id, github_repo, github_pull_id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -# Default pseudo packages (common for all projects) +--- Default pseudo packages (common for all projects) INSERT INTO bugdb_pseudo_packages SET id = '1', parent = '0', name = 'Web Site', long_name = 'Web Site', project = ''; INSERT INTO bugdb_pseudo_packages SET id = '2', parent = '1', name = 'Bug System', long_name = 'Bug System', project = ''; From 3388a5537392104e1a3706c554e04d9d1b71653c Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 23 Oct 2018 23:09:54 +0200 Subject: [PATCH 117/277] Enhance the package selection a bit Changes: - When first entering the report bug page, no package is selected - When selecting parent group, deselect childs - Increase the number of visibile packages to 12 --- www/js/package-affected.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/www/js/package-affected.js b/www/js/package-affected.js index 1d44fa8c..009afbd5 100644 --- a/www/js/package-affected.js +++ b/www/js/package-affected.js @@ -66,6 +66,8 @@ window.addEventListener( } moveOptions(nextGroup, select); + + select.selectedIndex = -1; } function moveOptions(from, to) { @@ -77,6 +79,7 @@ window.addEventListener( ); } + packageGroup.addEventListener('click', updateGroup); packageGroup.addEventListener('change', updateGroup); if (initialGroup instanceof HTMLOptGroupElement) { @@ -85,12 +88,13 @@ window.addEventListener( } else { select.disabled = true; select.value = null; + packageGroup.selectedIndex = -1; } packageGroup.style.marginRight = '.5em'; [select, packageGroup].forEach( function (element) { - element.size = 5; + element.size = 12; element.style.width = '22em'; } ); From 8249fa53342f9f13313493a64ff6f7caa5864a51 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 24 Oct 2018 18:28:17 +0200 Subject: [PATCH 118/277] Remove unused configuration db_extension The `db_extension` was once used with PEAR's MDB2. With migration to vanilla PDO, this is not used anymore. --- include/prepend.php | 1 - 1 file changed, 1 deletion(-) diff --git a/include/prepend.php b/include/prepend.php index 2e67f401..fce37e10 100644 --- a/include/prepend.php +++ b/include/prepend.php @@ -17,7 +17,6 @@ 'email' => 'php-bugs@lists.php.net', 'doc_email' => 'doc-bugs@lists.php.net', 'security_email' => 'security@php.net', - 'db_extension' => 'mysqli', 'db' => 'phpbugsdb', 'db_user' => 'nobody', 'db_pass' => '', From a06f85b0c3b775ed60292b680e7ce47e0c001bbe Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 24 Oct 2018 19:16:33 +0200 Subject: [PATCH 119/277] Refactor long array() syntax to short [] Since site is using PHP 5.4+ already, the longer `array()` syntax can be refactored to shorter `[]`. Also code is already using short array syntax on some places. --- include/classes/bug_ghpulltracker.php | 14 +-- include/classes/bug_patchtracker.php | 34 +++---- include/functions.php | 130 +++++++++++++------------- include/php_versions.php | 22 ++--- include/prepend.php | 4 +- include/query.php | 26 +++--- include/trusted-devs.php | 12 +-- local_config.php.sample | 4 +- scripts/cron/no-feedback | 8 +- www/admin/index.php | 16 ++-- www/api.php | 2 +- www/bug-pwd-finder.php | 4 +- www/bug.php | 20 ++-- www/fix.php | 10 +- www/gh-pull-add.php | 6 +- www/index.php | 4 +- www/js/userlisting.php | 10 +- www/lstats.php | 6 +- www/patch-add.php | 10 +- www/report.php | 14 +-- www/rpc.php | 18 ++-- www/stats.php | 16 ++-- www/vote.php | 8 +- 23 files changed, 199 insertions(+), 199 deletions(-) diff --git a/include/classes/bug_ghpulltracker.php b/include/classes/bug_ghpulltracker.php index 47d1524b..3ad968ce 100644 --- a/include/classes/bug_ghpulltracker.php +++ b/include/classes/bug_ghpulltracker.php @@ -13,12 +13,12 @@ function __construct() private function getDataFromGithub($repo, $pull_id) { - $ctxt = stream_context_create(array( - 'http' => array( + $ctxt = stream_context_create([ + 'http' => [ 'ignore_errors' => '1', 'user_agent' => $this->userAgent, - ) - )); + ] + ]); $data = @json_decode(file_get_contents("https://api.github.com/repos/php/".urlencode($repo).'/pulls/'.((int)$pull_id), null, $ctxt)); if (!is_object($data)) { return false; @@ -38,7 +38,7 @@ function attach($bugid, $repo, $pull_id, $developer) PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $e = $this->_dbh->prepare('INSERT INTO bugdb_pulls (bugdb_id, github_repo, github_pull_id, github_title, github_html_url, developer) VALUES (?, ?, ?, ?, ?, ?)')->execute( - array($bugid, $repo, $pull_id, $data->title, $data->html_url, $developer)); + [$bugid, $repo, $pull_id, $data->title, $data->html_url, $developer]); PEAR::popErrorHandling(); if (PEAR::isError($e)) { return $e; @@ -54,7 +54,7 @@ function detach($bugid, $repo, $pull_id) { $this->_dbh->prepare('DELETE FROM bugdb_pulls WHERE bugdb_id = ? and github_repo = ? and github_pull_id = ?')->execute( - array($bugid, $repo, $pull_id)); + [$bugid, $repo, $pull_id]); } /** @@ -72,6 +72,6 @@ function listPulls($bugid) ORDER BY github_repo, github_pull_id DESC '; - return $this->_dbh->prepare($query)->execute(array($bugid))->fetchAll(PDO::FETCH_ASSOC); + return $this->_dbh->prepare($query)->execute([$bugid])->fetchAll(PDO::FETCH_ASSOC); } } diff --git a/include/classes/bug_patchtracker.php b/include/classes/bug_patchtracker.php index 2324ecaf..a66f49ad 100644 --- a/include/classes/bug_patchtracker.php +++ b/include/classes/bug_patchtracker.php @@ -76,19 +76,19 @@ function newPatchFileName($bugid, $patch, $handle) PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $e = $this->_dbh->prepare('INSERT INTO bugdb_patchtracker (bugdb_id, patch, revision, developer) VALUES(?, ?, ?, ?)')->execute( - array($bugid, $patch, $id, $handle)); + [$bugid, $patch, $id, $handle]); if (PEAR::isError($e)) { // try with another timestamp $id++; $e = $this->_dbh->prepare('INSERT INTO bugdb_patchtracker (bugdb_id, patch, revision, developer) VALUES(?, ?, ?, ?)')->execute( - array($bugid, $patch, $id, $handle)); + [$bugid, $patch, $id, $handle]); } PEAR::popErrorHandling(); if (PEAR::isError($e)) { return PEAR::raiseError("Could not get unique patch file name for bug #{$bugid}, patch \"{$patch}\""); } - return array($id, $this->getPatchFileName($id)); + return [$id, $this->getPatchFileName($id)]; } /** @@ -144,7 +144,7 @@ function attach($bugid, $patch, $name, $handle, $obsoletes) } if ($file->isValid()) { - $newobsoletes = array(); + $newobsoletes = []; foreach ($obsoletes as $who) { if (!$who) { continue; // remove (none) @@ -167,7 +167,7 @@ function attach($bugid, $patch, $name, $handle, $obsoletes) } list($id, $fname) = $res; $file->setName($fname); - $allowed_mime_types = array( + $allowed_mime_types = [ 'application/x-txt', 'text/plain', 'text/x-diff', @@ -175,7 +175,7 @@ function attach($bugid, $patch, $name, $handle, $obsoletes) 'text/x-c++', 'text/x-c', 'text/x-m4', - ); + ]; // return mime type ala mimetype extension if (class_exists('finfo')) { @@ -197,7 +197,7 @@ function attach($bugid, $patch, $name, $handle, $obsoletes) if (!in_array($mime, $allowed_mime_types)) { $this->_dbh->prepare('DELETE FROM bugdb_patchtracker WHERE bugdb_id = ? and patch = ? and revision = ?')->execute( - array($bugid, $name, $id)); + [$bugid, $name, $id]); return PEAR::raiseError('Error: uploaded patch file must be text' . ' file (save as e.g. "patch.txt" or "package.diff")' . ' (detected "' . htmlspecialchars($mime) . '")' @@ -207,7 +207,7 @@ function attach($bugid, $patch, $name, $handle, $obsoletes) if (PEAR::isError($tmpfile)) { $this->_dbh->prepare('DELETE FROM bugdb_patchtracker WHERE bugdb_id = ? and patch = ? and revision = ?')->execute( - array($bugid, $name, $id)); + [$bugid, $name, $id]); return $tmpfile; } if (!$file->getProp('size')) { @@ -241,7 +241,7 @@ function detach($bugid, $name, $revision) { $this->_dbh->prepare('DELETE FROM bugdb_patchtracker WHERE bugdb_id = ? and patch = ? and revision = ?')->execute( - array($bugid, $name, $revision)); + [$bugid, $name, $revision]); @unlink($this->patchDir($bugid, $name) . DIRECTORY_SEPARATOR . $this->getPatchFileName($revision)); } @@ -259,7 +259,7 @@ function getPatch($bugid, $name, $revision) if ($this->_dbh->prepare(' SELECT bugdb_id FROM bugdb_patchtracker - WHERE bugdb_id = ? AND patch = ? AND revision = ?')->execute(array($bugid, $name, $revision))->fetchOne() + WHERE bugdb_id = ? AND patch = ? AND revision = ?')->execute([$bugid, $name, $revision])->fetchOne() ) { $contents = @file_get_contents($this->getPatchFullpath($bugid, $name, $revision)); if (!$contents) { @@ -285,7 +285,7 @@ function listPatches($bugid) ORDER BY revision DESC '; - return $this->_dbh->prepare($query)->execute(array($bugid))->fetchAll(PDO::FETCH_NUM, true, false, true); + return $this->_dbh->prepare($query)->execute([$bugid])->fetchAll(PDO::FETCH_NUM, true, false, true); } /** @@ -302,7 +302,7 @@ function listRevisions($bugid, $patch) WHERE bugdb_id = ? AND patch = ? ORDER BY revision DESC '; - return $this->_dbh->prepare($query)->execute(array($bugid, $patch))->fetchAll(PDO::FETCH_NUM); + return $this->_dbh->prepare($query)->execute([$bugid, $patch])->fetchAll(PDO::FETCH_NUM); } /** @@ -320,13 +320,13 @@ function getDeveloper($bugid, $patch, $revision = false) SELECT developer FROM bugdb_patchtracker WHERE bugdb_id = ? AND patch = ? AND revision = ? - ')->execute(array($bugid, $patch, $revision))->fetchOne(); + ')->execute([$bugid, $patch, $revision])->fetchOne(); } return $this->_dbh->prepare(' SELECT developer, revision FROM bugdb_patchtracker WHERE bugdb_id = ? AND patch = ? ORDER BY revision DESC - ')->execute(array($bugid, $patch))->fetchAll(PDO::FETCH_ASSOC); + ')->execute([$bugid, $patch])->fetchAll(PDO::FETCH_ASSOC); } function getObsoletingPatches($bugid, $patch, $revision) @@ -335,7 +335,7 @@ function getObsoletingPatches($bugid, $patch, $revision) SELECT bugdb_id, patch, revision FROM bugdb_obsoletes_patches WHERE bugdb_id = ? AND obsolete_patch = ? AND obsolete_revision = ? - ')->execute(array($bugid, $patch, $revision))->fetchAll(PDO::FETCH_ASSOC); + ')->execute([$bugid, $patch, $revision])->fetchAll(PDO::FETCH_ASSOC); } function getObsoletePatches($bugid, $patch, $revision) @@ -344,7 +344,7 @@ function getObsoletePatches($bugid, $patch, $revision) SELECT bugdb_id, obsolete_patch, obsolete_revision FROM bugdb_obsoletes_patches WHERE bugdb_id = ? AND patch = ? AND revision = ? - ')->execute(array($bugid, $patch, $revision))->fetchAll(PDO::FETCH_ASSOC); + ')->execute([$bugid, $patch, $revision])->fetchAll(PDO::FETCH_ASSOC); } /** @@ -361,6 +361,6 @@ function obsoletePatch($bugid, $name, $revision, $obsoletename, $obsoleterevisio $this->_dbh->prepare(' INSERT INTO bugdb_obsoletes_patches VALUES(?, ?, ?, ?, ?) - ')->execute(array($bugid, $name, $revision, $obsoletename, $obsoleterevision)); + ')->execute([$bugid, $name, $revision, $obsoletename, $obsoleterevision]); } } diff --git a/include/functions.php b/include/functions.php index 101e8d97..13b9c649 100644 --- a/include/functions.php +++ b/include/functions.php @@ -9,7 +9,7 @@ /* Contains functions and variables used throughout the bug system */ // used in mail_bug_updates(), below, and class for search results -$tla = array( +$tla = [ 'Open' => 'Opn', 'Not a bug' => 'Nab', 'Feedback' => 'Fbk', @@ -24,22 +24,22 @@ 'Closed' => 'Csd', 'Spam' => 'Spm', 'Re-Opened' => 'ReO', -); +]; -$bug_types = array( +$bug_types = [ 'Bug' => 'Bug', 'Feature/Change Request' => 'Req', 'Documentation Problem' => 'Doc', 'Security' => 'Sec Bug' -); +]; -$project_types = array( +$project_types = [ 'PHP' => 'php', 'PECL' => 'pecl' -); +]; // Used in show_state_options() -$state_types = array ( +$state_types = [ 'Open' => 2, 'Closed' => 2, 'Re-Opened' => 1, @@ -59,7 +59,7 @@ 'Not a bug' => 1, 'Spam' => 1, 'All' => 0, -); +]; /** * Authentication @@ -69,20 +69,20 @@ function verify_user_password($user, $pass) global $errors; $post = http_build_query( - array( + [ 'token' => getenv('AUTH_TOKEN'), 'username' => $user, 'password' => $pass, - ) + ] ); - $opts = array( + $opts = [ 'method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'content' => $post, - ); + ]; - $ctx = stream_context_create(array('http' => $opts)); + $ctx = stream_context_create(['http' => $opts]); $s = file_get_contents('https://master.php.net/fetch/cvsauth.php', false, $ctx); @@ -204,7 +204,7 @@ function get_pseudo_packages($project, $return_disabled = true) { global $dbh, $project_types; - $pseudo_pkgs = $nodes = $tree = array(); + $pseudo_pkgs = $nodes = $tree = []; $where = '1=1'; $project = strtolower($project); @@ -220,7 +220,7 @@ function get_pseudo_packages($project, $return_disabled = true) // Convert flat array to nested strucutre foreach ($data as &$node) { - $node['children'] = array(); + $node['children'] = []; $id = $node['id']; $parent_id = $node['parent']; $nodes[$id] =& $node; @@ -235,21 +235,21 @@ function get_pseudo_packages($project, $return_disabled = true) foreach ($tree as $data) { if (isset($data['children'])) { - $pseudo_pkgs[$data['name']] = array($data['long_name'], $data['disabled'], array()); + $pseudo_pkgs[$data['name']] = [$data['long_name'], $data['disabled'], []]; $children = &$pseudo_pkgs[$data['name']][2]; - $long_names = array(); + $long_names = []; foreach ($data['children'] as $k => $v) { $long_names[$k] = strtolower($v['long_name']); } array_multisort($long_names, SORT_ASC, SORT_STRING, $data['children']); foreach ($data['children'] as $child) { - $pseudo_pkgs[$child['name']] = array("{$child['long_name']}", $child['disabled'], null); + $pseudo_pkgs[$child['name']] = ["{$child['long_name']}", $child['disabled'], null]; $children[] = $child['name']; } } elseif (!isset($pseudo_pkgs[$data['name']])) { - $pseudo_pkgs[$data['name']] = array($data['long_name'], $data['disabled'], null); + $pseudo_pkgs[$data['name']] = [$data['long_name'], $data['disabled'], null]; } } @@ -268,7 +268,7 @@ function is_spam($string) return true; } - $keywords = array( + $keywords = [ 'spy', 'bdsm', 'massage', @@ -303,7 +303,7 @@ function is_spam($string) 'jerseys', 'wholesale', 'fashionretailshop01', - ); + ]; if (preg_match('/\b('. implode('|', $keywords) . ')\b/i', $string)) { return true; @@ -331,15 +331,15 @@ function spam_protect($txt, $format = 'html') return $txt; } if ($format == 'html') { - $translate = array( + $translate = [ '@' => ' at ', '.' => ' dot ', - ); + ]; } else { - $translate = array( + $translate = [ '@' => ' at ', '.' => ' dot ', - ); + ]; if ($format == 'reverse') { $translate = array_flip($translate); } @@ -361,7 +361,7 @@ function escapeSQL($in) global $dbh; if (is_array($in)) { - $out = array(); + $out = []; foreach ($in as $key => $value) { $out[$key] = $dbh->escape($value); } @@ -424,7 +424,7 @@ function field($n) function clean($in) { return mb_encode_numericentity($in, - array( + [ 0x0, 0x8, 0, 0xffffff, 0xb, 0xc, 0, 0xffffff, 0xe, 0x1f, 0, 0xffffff, @@ -451,7 +451,7 @@ function clean($in) 0xefffe, 0xeffff, 0, 0xffffff, 0xffffe, 0xfffff, 0, 0xffffff, 0x10fffe, 0x10ffff, 0, 0xffffff, - ), + ], 'UTF-8'); } @@ -490,14 +490,14 @@ function txfield($n, $bug = null, $in = null) */ function show_byage_options($current) { - $opts = array( + $opts = [ '0' => 'the beginning', '1' => 'yesterday', '7' => '7 days ago', '15' => '15 days ago', '30' => '30 days ago', '90' => '90 days ago', - ); + ]; while (list($k,$v) = each($opts)) { echo "\n"; } @@ -783,7 +783,7 @@ function ($value) { */ function show_boolean_options($current) { - $options = array('any', 'all', 'raw'); + $options = ['any', 'all', 'raw']; while (list($val, $type) = each($options)) { echo ' 'Summary', 'status' => 'Status', 'bug_type' => 'Type', @@ -861,14 +861,14 @@ function bug_diff($bug, $in) 'block_user_comment' => 'Block user comment', 'private' => 'Private report', 'cve_id' => 'CVE-ID' - ); + ]; foreach (array_keys($fields) as $name) { if (array_key_exists($name, $in) && array_key_exists($name, $bug)) { $to = trim($in[$name]); $from = trim($bug[$name]); if ($from != $to) { - if (in_array($name, array('private', 'block_user_comment'))) { + if (in_array($name, ['private', 'block_user_comment'])) { $from = $from == 'Y' ? 'Yes' : 'No'; $to = $to == 'Y' ? 'Yes' : 'No'; } @@ -883,7 +883,7 @@ function bug_diff($bug, $in) function bug_diff_render_html($diff) { - $fields = array( + $fields = [ 'sdesc' => 'Summary', 'status' => 'Status', 'bug_type' => 'Type', @@ -894,7 +894,7 @@ function bug_diff_render_html($diff) 'block_user_comment' => 'Block user comment', 'private' => 'Private report', 'cve_id' => 'CVE-ID' - ); + ]; // make diff output aligned $actlength = $maxlength = 0; @@ -932,35 +932,35 @@ function mail_bug_updates($bug, $in, $from, $ncomment, $edit = 1, $id = false) { global $tla, $bug_types, $siteBig, $site_method, $site_url, $basedir; - $text = array(); - $headers = array(); + $text = []; + $headers = []; $changed = bug_diff($bug, $in); - $from = str_replace(array("\n", "\r"), '', $from); + $from = str_replace(["\n", "\r"], '', $from); /* Default addresses */ list($mailto, $mailfrom, $bcc, $params) = get_package_mail(oneof(@$in['package_name'], $bug['package_name']), $id, oneof(@$in['bug_type'], $bug['bug_type'])); - $headers[] = array(' ID', $bug['id']); + $headers[] = [' ID', $bug['id']]; switch ($edit) { case 4: - $headers[] = array(' Patch added by', $from); + $headers[] = [' Patch added by', $from]; $from = "\"{$from}\" <{$mailfrom}>"; break; case 3: - $headers[] = array(' Comment by', $from); + $headers[] = [' Comment by', $from]; $from = "\"{$from}\" <{$mailfrom}>"; break; case 2: $from = spam_protect(txfield('email', $bug, $in), 'text'); - $headers[] = array(' User updated by', $from); + $headers[] = [' User updated by', $from]; $from = "\"{$from}\" <{$mailfrom}>"; break; default: - $headers[] = array(' Updated by', $from); + $headers[] = [' Updated by', $from]; } - $fields = array( + $fields = [ 'email' => 'Reported by', 'sdesc' => 'Summary', 'status' => 'Status', @@ -972,12 +972,12 @@ function mail_bug_updates($bug, $in, $from, $ncomment, $edit = 1, $id = false) 'block_user_comment' => 'Block user comment', 'private' => 'Private report', 'cve_id' => 'CVE-ID', - ); + ]; foreach ($fields as $name => $desc) { $prefix = ' '; if (isset($changed[$name])) { - $headers[] = array("-{$desc}", $changed[$name]['from']); + $headers[] = ["-{$desc}", $changed[$name]['from']]; $prefix = '+'; } @@ -987,7 +987,7 @@ function mail_bug_updates($bug, $in, $from, $ncomment, $edit = 1, $id = false) $f = spam_protect($f, 'text'); } $foo = isset($changed[$name]['to']) ? $changed[$name]['to'] : $f; - $headers[] = array($prefix.$desc, $foo); + $headers[] = [$prefix.$desc, $foo]; } } @@ -1092,7 +1092,7 @@ function mail_bug_updates($bug, $in, $from, $ncomment, $edit = 1, $id = false) $tmp = $edit != 3 ? $in : $bug; $tmp['new_status'] = $new_status; $tmp['old_status'] = $old_status; - foreach (array('bug_type', 'php_version', 'package_name', 'php_os') as $field) { + foreach (['bug_type', 'php_version', 'package_name', 'php_os'] as $field) { $tmp[$field] = strtok($tmp[$field], "\r\n"); } @@ -1285,7 +1285,7 @@ function incoming_details_are_valid($in, $initial = 0, $logged_in = false) { global $bug, $dbh, $bug_types, $versions; - $errors = array(); + $errors = []; if (!is_array($in)) { $errors[] = 'Invalid data submitted!'; return $errors; @@ -1339,7 +1339,7 @@ function get_package_mail($package_name, $bug_id = false, $bug_type = 'Bug') { global $dbh, $bugEmail, $docBugEmail, $secBugEmail, $security_distro_people; - $to = array(); + $to = []; $params = '-f noreply@php.net'; $mailfrom = $bugEmail; @@ -1406,9 +1406,9 @@ function get_package_mail($package_name, $bug_id = false, $bug_type = 'Bug') $bcc = $dbh->prepare("SELECT email FROM bugdb_subscribe WHERE bug_id=?")->execute([$bug_id])->fetchAll(); $bcc = array_unique($bcc); - return array(implode(', ', $to), $mailfrom, implode(', ', $bcc), $params); + return [implode(', ', $to), $mailfrom, implode(', ', $bcc), $params]; } else { - return array(implode(', ', $to), $mailfrom, '', $params); + return [implode(', ', $to), $mailfrom, '', $params]; } } @@ -1426,7 +1426,7 @@ function format_search_string($search, $boolean_search = false) $min_word_len=3; $words = preg_split("/\s+/", $search); - $ignored = $used = array(); + $ignored = $used = []; foreach($words as $match) { if (strlen($match) < $min_word_len) { @@ -1443,15 +1443,15 @@ function format_search_string($search, $boolean_search = false) foreach ($used as $word) { $newsearch .= "+$word "; } - return array(" AND MATCH (bugdb.email,sdesc,ldesc) AGAINST ('" . escapeSQL($newsearch) . "' IN BOOLEAN MODE)", $ignored); + return [" AND MATCH (bugdb.email,sdesc,ldesc) AGAINST ('" . escapeSQL($newsearch) . "' IN BOOLEAN MODE)", $ignored]; // allow custom boolean search (raw) } elseif ($boolean_search === 2) { - return array(" AND MATCH (bugdb.email,sdesc,ldesc) AGAINST ('" . escapeSQL($search) . "' IN BOOLEAN MODE)", $ignored); + return [" AND MATCH (bugdb.email,sdesc,ldesc) AGAINST ('" . escapeSQL($search) . "' IN BOOLEAN MODE)", $ignored]; } } // require any of the words (any) - return array(" AND MATCH (bugdb.email,sdesc,ldesc) AGAINST ('" . escapeSQL($search) . "')", $ignored); + return [" AND MATCH (bugdb.email,sdesc,ldesc) AGAINST ('" . escapeSQL($search) . "')", $ignored]; } /** @@ -1568,8 +1568,8 @@ function get_resolve_reasons($project = false) $where.= "WHERE (project = '{$project}' OR project = '')"; } - $resolves = $variations = array(); - $res = $dbh->prepare("SELECT * FROM bugdb_resolves $where")->execute(array()); + $resolves = $variations = []; + $res = $dbh->prepare("SELECT * FROM bugdb_resolves $where")->execute([]); if (!$res) { throw new Exception("SQL Error in get_resolve_reasons"); } @@ -1580,7 +1580,7 @@ function get_resolve_reasons($project = false) $resolves[$row['name']] = $row; } } - return array($resolves, $variations); + return [$resolves, $variations]; } /** @@ -1641,9 +1641,9 @@ function bugs_add_comment($bug_id, $from, $from_name, $comment, $type = 'comment return $dbh->prepare(" INSERT INTO bugdb_comments (bug, email, reporter_name, comment, comment_type, ts, visitor_ip) VALUES (?, ?, ?, ?, ?, NOW(), INET6_ATON(?)) - ")->execute(array( + ")->execute([ $bug_id, $from, $from_name, $comment, $type, (!empty($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:'127.0.0.1') - )); + ]); } /** @@ -1878,7 +1878,7 @@ function make_ticket_links($text) function get_ticket_links($text) { - $matches = array(); + $matches = []; preg_match_all('/(?a-z])(?:bug(?:fix)?|feat(?:ure)?|doc(?:umentation)?|req(?:uest)?|duplicated of)\s+#?([0-9]+)/i', $text, $matches); diff --git a/include/php_versions.php b/include/php_versions.php index 46ebc75e..88600497 100644 --- a/include/php_versions.php +++ b/include/php_versions.php @@ -12,11 +12,11 @@ */ // Custom versions appended to the list - $custom_versions = array( + $custom_versions = [ 'Next Major Version', 'Next Minor Version', 'Irrelevant' - ); + ]; if(!file_exists("/tmp/versions.php") || filemtime("/tmp/versions.php") < $_SERVER['REQUEST_TIME'] - 3600) { $versions = buildVersions(); @@ -31,13 +31,13 @@ function buildVersions() { $dev_versions = json_decode(file_get_contents('https://qa.php.net/api.php?type=qa-releases&format=json&only=dev_versions')); - $versions = array(); + $versions = []; $date = date('Y-m-d'); - $default_versions = array( + $default_versions = [ "Git-{$date} (snap)", "Git-{$date} (Git)", - ); + ]; foreach ($dev_versions as $dev_version) { $dev_version_parts = parseVersion($dev_version); @@ -45,7 +45,7 @@ function buildVersions() { // if it is a dev version, then add that branch, add the minor-1 version, if it's appropriate if ($dev_version_parts['type'] == 'dev') { if (!isset($versions[$dev_version_parts['major']][$dev_version_parts['minor']])) { - $versions[$dev_version_parts['major']][$dev_version_parts['minor']] = array(); + $versions[$dev_version_parts['major']][$dev_version_parts['minor']] = []; } } // then it is a qa version (alpha|beta|rc) @@ -64,7 +64,7 @@ function buildVersions() { } } - $flat_versions = array(); + $flat_versions = []; // add master to the end of the list foreach ($default_versions as $default_version) { @@ -92,16 +92,16 @@ function buildVersions() { function parseVersion($version){ - $version_parts = array(); - $raw_parts = array(); + $version_parts = []; + $raw_parts = []; preg_match('#(?P\d+)\.(?P\d+).(?P\d+)[-]?(?PRC|alpha|beta|dev)?(?P[\d]?).*#ui', $version, $raw_parts); - $version_parts = array( + $version_parts = [ 'major' => $raw_parts['major'], 'minor' => $raw_parts['minor'], 'micro' => $raw_parts['micro'], 'type' => strtolower($raw_parts['type']?$raw_parts['type']:'stable'), 'number' => $raw_parts['number'], 'original_version' => $version, - ); + ]; return $version_parts; } diff --git a/include/prepend.php b/include/prepend.php index fce37e10..367510ff 100644 --- a/include/prepend.php +++ b/include/prepend.php @@ -10,7 +10,7 @@ if (file_exists($local_cfg)) { require $local_cfg; } else { - $site_data = array ( + $site_data = [ 'method' => 'https', 'url' => 'bugs.php.net', 'basedir' => '', @@ -22,7 +22,7 @@ 'db_pass' => '', 'db_host' => 'localhost', 'patch_tmp' => "{$ROOT_DIR}/uploads/patches/", - ); + ]; define('DEVBOX', false); } // CONFIG END diff --git a/include/query.php b/include/query.php index 8486b415..3e051a91 100644 --- a/include/query.php +++ b/include/query.php @@ -1,8 +1,8 @@ 'relevance', 'id' => 'ID', 'ts1' => 'date', @@ -17,7 +17,7 @@ 'avg_score' => 'avg. vote score', 'votes_count' => 'number of votes', 'RAND()' => 'random', -); +]; // Fetch pseudo packages $pseudo_pkgs = get_pseudo_packages(false); @@ -48,8 +48,8 @@ $reorder_by = (!empty($_GET['reorder_by']) && array_key_exists($_GET['reorder_by'], $order_options)) ? $_GET['reorder_by'] : ''; $assign = !empty($_GET['assign']) ? $_GET['assign'] : ''; $author_email = !empty($_GET['author_email']) ? spam_protect($_GET['author_email'], 'reverse') : ''; -$package_name = (isset($_GET['package_name']) && is_array($_GET['package_name'])) ? $_GET['package_name'] : array(); -$package_nname = (isset($_GET['package_nname']) && is_array($_GET['package_nname'])) ? $_GET['package_nname'] : array(); +$package_name = (isset($_GET['package_name']) && is_array($_GET['package_name'])) ? $_GET['package_name'] : []; +$package_nname = (isset($_GET['package_nname']) && is_array($_GET['package_nname'])) ? $_GET['package_nname'] : []; $commented_by = !empty($_GET['commented_by']) ? spam_protect($_GET['commented_by'], 'reverse') : ''; if (isset($_GET['cmd']) && $_GET['cmd'] == 'display') @@ -63,7 +63,7 @@ FROM bugdb '; - if (in_array($order_by, array('votes_count', 'avg_score'))) { + if (in_array($order_by, ['votes_count', 'avg_score'])) { $query .= 'LEFT JOIN bugdb_votes v ON bugdb.id = v.bug'; } @@ -229,23 +229,23 @@ } } - $order_by_clauses = array(); - if (in_array($order_by, array('votes_count', 'avg_score'))) { + $order_by_clauses = []; + if (in_array($order_by, ['votes_count', 'avg_score'])) { $query .= ' GROUP BY bugdb.id'; switch ($order_by) { case 'avg_score': - $order_by_clauses = array( + $order_by_clauses = [ "IFNULL(AVG(v.score), 0)+3 $direction", "COUNT(v.bug) DESC" - ); + ]; break; case 'votes_count': - $order_by_clauses = array("COUNT(v.bug) $direction"); + $order_by_clauses = ["COUNT(v.bug) $direction"]; break; } } elseif ($order_by != '') { - $order_by_clauses = array("$order_by $direction"); + $order_by_clauses = ["$order_by $direction"]; } if ($status == 'Feedback') { diff --git a/include/trusted-devs.php b/include/trusted-devs.php index 5a045515..465c8384 100644 --- a/include/trusted-devs.php +++ b/include/trusted-devs.php @@ -1,6 +1,6 @@ 'https', 'url' => 'bugs.php.net', 'basedir' => '/bugs', @@ -16,6 +16,6 @@ $site_data = array ( 'db_pass' => '', 'db_host' => 'localhost', 'patch_tmp' => "{$ROOT_DIR}/uploads/patches/", -); +]; define('DEVBOX', true); diff --git a/scripts/cron/no-feedback b/scripts/cron/no-feedback index a6d263b2..91ad4ad1 100755 --- a/scripts/cron/no-feedback +++ b/scripts/cron/no-feedback @@ -9,7 +9,7 @@ require __DIR__.'/../../include/prepend.php'; $after = "7 DAY"; # Set "input" array -$in = array('status' => 'No Feedback'); +$in = ['status' => 'No Feedback']; # Update relevant reports if ($dbh) @@ -22,7 +22,7 @@ if ($dbh) private, reporter_name, UNIX_TIMESTAMP(ts2) AS modified FROM bugdb WHERE status = 'Feedback' AND ts2 < DATE_SUB(NOW(), INTERVAL {$after}) - ")->execute(array()); + ")->execute([]); if (PEAR::isError($res)) { throw new Exception("SQL Error in no-feedback"); } @@ -45,9 +45,9 @@ if ($dbh) UPDATE bugdb SET status = "No Feedback", ts2 = NOW() WHERE id = ? - ')->execute(array( + ')->execute([ $bug['id'], - )); + ]); // Send emails mail_bug_updates($bug, $in, $mailfrom, $message); diff --git a/www/admin/index.php b/www/admin/index.php index 5d60eb00..e6f381b9 100644 --- a/www/admin/index.php +++ b/www/admin/index.php @@ -10,12 +10,12 @@ exit; } -$actions = array( +$actions = [ 'phpinfo' => 'phpinfo()', 'list_lists' => 'Package mailing lists', 'list_responses' => 'Quick fix responses', 'mysql' => 'Database status', -); +]; $action = !empty($_GET['action']) && isset($actions[$_GET['action']]) ? $_GET['action'] : 'list_lists'; @@ -29,16 +29,16 @@ $phpinfo = ob_get_clean(); // Attempt to hide certain ENV vars - $vars = array( + $vars = [ getenv('AUTH_TOKEN'), getenv('USER_TOKEN'), getenv('USER_PWD_SALT') - ); + ]; $phpinfo = str_replace($vars, '<hidden>', $phpinfo); // Semi stolen from php-web - $m = array(); + $m = []; preg_match('!(.*)!ims', $phpinfo, $m); @@ -72,7 +72,7 @@ echo "

    List Responses

    \n"; - $rows = array(); + $rows = []; while ($row = $res->fetchRow(PDO::FETCH_ASSOC)) { // This is ugly but works (tm) $row['message'] = nl2br($row['message']); @@ -99,7 +99,7 @@ echo "

    Number of rows:

    \n"; - $rows = array(); + $rows = []; foreach($row as $key => $value) { $rows[] = [str_replace('cnt_', '', $key), $value]; @@ -107,7 +107,7 @@ admin_table_static(['Table', 'Rows'], $rows); - $rows = array(); + $rows = []; $res = $dbh->query("SHOW TABLE STATUS"); echo "

    Table status:

    \n"; while ($row = $res->fetchRow(PDO::FETCH_ASSOC)) { diff --git a/www/api.php b/www/api.php index b1e2e17a..4c664ee8 100644 --- a/www/api.php +++ b/www/api.php @@ -27,7 +27,7 @@ "; //@todo add error handling - $rows = $dbh->prepare($query)->execute(array())->fetchAll(PDO::FETCH_ASSOC); + $rows = $dbh->prepare($query)->execute([])->fetchAll(PDO::FETCH_ASSOC); if (!$rows) { echo 'The fail train has arrived.'; exit; diff --git a/www/bug-pwd-finder.php b/www/bug-pwd-finder.php index eb78c543..08a0feab 100644 --- a/www/bug-pwd-finder.php +++ b/www/bug-pwd-finder.php @@ -11,7 +11,7 @@ // Obtain common includes require_once '../include/prepend.php'; -$errors = array(); +$errors = []; $success = false; $bug_id = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0; $bug_id = $bug_id ? $bug_id : ''; @@ -42,7 +42,7 @@ 'UPDATE bugdb SET passwd = ? WHERE id = ? - ')->execute(array(bugs_get_hash($new_passwd), $bug_id)); + ')->execute([bugs_get_hash($new_passwd), $bug_id]); $resp = bugs_mail($row['email'], "Password for {$siteBig} bug report #{$bug_id}", diff --git a/www/bug.php b/www/bug.php index 84d8d539..a089041d 100644 --- a/www/bug.php +++ b/www/bug.php @@ -32,7 +32,7 @@ } // Init common variables -$errors = array(); +$errors = []; // Set edit mode $edit = isset($_REQUEST['edit']) ? (int) $_REQUEST['edit'] : 0; @@ -86,7 +86,7 @@ } else // Subscribe { - $dbh->prepare('REPLACE INTO bugdb_subscribe SET bug_id = ?, email = ?')->execute(array($bug_id, $email)); + $dbh->prepare('REPLACE INTO bugdb_subscribe SET bug_id = ?, email = ?')->execute([$bug_id, $email]); $thanks = 7; } redirect("bug.php?id={$bug_id}&thanks={$thanks}"); @@ -169,7 +169,7 @@ $is_private = isset($is_private) ? $is_private : $bug['private']; // Handle any updates, displaying errors if there were any -$RESOLVE_REASONS = $FIX_VARIATIONS = $pseudo_pkgs = array(); +$RESOLVE_REASONS = $FIX_VARIATIONS = $pseudo_pkgs = []; $project = $bug['project']; @@ -332,7 +332,7 @@ ts2 = NOW(), private = ? WHERE id={$bug_id} - ")->execute(array( + ")->execute([ $_POST['in']['sdesc'], $_POST['in']['status'], $_POST['in']['package_name'], @@ -341,7 +341,7 @@ $_POST['in']['php_os'], $from, $is_private - )); + ]); // Add changelog entry $changed = bug_diff($bug, $_POST['in']); @@ -415,7 +415,7 @@ $errors[] = "You must provide a status"; } else { if ($_POST['in']['status'] == 'Not a bug' && - !in_array($bug['status'], array ('Not a bug', 'Closed', 'Duplicate', 'No feedback', 'Wont fix')) && + !in_array($bug['status'], ['Not a bug', 'Closed', 'Duplicate', 'No feedback', 'Wont fix']) && strlen(trim($ncomment)) == 0 ) { $errors[] = "You must provide a comment when marking a bug 'Not a bug'"; @@ -506,7 +506,7 @@ private = ?, ts2 = NOW() WHERE id = {$bug_id} - ")->execute(array ( + ")->execute([ $_POST['in']['sdesc'], $status, $_POST['in']['package_name'], @@ -517,7 +517,7 @@ $block_user, $_POST['in']['cve_id'], $is_private - )); + ]); // Add changelog entry $changed = bug_diff($bug, $_POST['in']); @@ -1097,13 +1097,13 @@ // Display comments $bug_comments = bugs_get_bug_comments($bug_id); if ($show_bug_info && is_array($bug_comments) && count($bug_comments) && $bug['status'] !== 'Spam') { - $history_tabs = array( + $history_tabs = [ 'type_all' => 'All', 'type_comment' => 'Comments', 'type_log' => 'Changes', 'type_svn' => 'Git/SVN commits', 'type_related' => 'Related reports' - ); + ]; if (!isset($_COOKIE['history_tab']) || !isset($history_tabs[$_COOKIE['history_tab']])) { $active_history_tab = 'type_all'; diff --git a/www/fix.php b/www/fix.php index 2834805e..3006bf98 100644 --- a/www/fix.php +++ b/www/fix.php @@ -27,7 +27,7 @@ } // If bug exists, continue.. -$RESOLVE_REASONS = $FIX_VARIATIONS = $errors = array(); +$RESOLVE_REASONS = $FIX_VARIATIONS = $errors = []; $is_trusted_developer = ($user_flags & BUGS_TRUSTED_DEV); @@ -118,13 +118,13 @@ } // Standard items -$in = array( +$in = [ 'status' => $status, 'bug_type' => $bug['bug_type'], 'php_version' => $bug['php_version'], 'php_os' => $bug['php_os'], 'assign' => $bug['assign'], -); +]; // Assign automatically when closed if ($status == 'Closed' && $in['assign'] == '') { @@ -139,11 +139,11 @@ assign = ?, ts2 = NOW() WHERE id = ? -")->execute(array ( +")->execute([ $status, $in['assign'], $bug_id, -)); +]); // Add changelog entry if (!PEAR::isError($res)) { diff --git a/www/gh-pull-add.php b/www/gh-pull-add.php index b1fe0682..616dda54 100644 --- a/www/gh-pull-add.php +++ b/www/gh-pull-add.php @@ -54,7 +54,7 @@ $pullinfo = new Bug_Pulltracker; if (isset($_POST['addpull'])) { - $errors = array(); + $errors = []; if (empty($_POST['repository'])) { $errors[] = 'No repository selected'; } @@ -97,7 +97,7 @@ $newpr = $pullinfo->attach($bug_id, $_POST['repository'], $_POST['pull_id'], $email); PEAR::popErrorHandling(); if (PEAR::isError($newpr)) { - $errors = array($newpr->getMessage(), 'Could not attach pull request to Bug #' . $bug_id); + $errors = [$newpr->getMessage(), 'Could not attach pull request to Bug #' . $bug_id]; } } @@ -123,7 +123,7 @@ mail_bug_updates($buginfo, $buginfo, $auth_user->email, $text, 4, $bug_id); */ $pulls = $pullinfo->listPulls($bug_id); - $errors = array(); + $errors = []; include "{$ROOT_DIR}/templates/addghpull.php"; exit; } diff --git a/www/index.php b/www/index.php index 58d78524..a43a38a3 100644 --- a/www/index.php +++ b/www/index.php @@ -95,7 +95,7 @@ '&bug_type=All', 'Most recent open bugs (all) with patch or pull request' => '&bug_type=All&patch=Y&pull=Y', 'Most recent open bugs (PHP 5.6)' => '&bug_type=All&phpver=5.6', @@ -105,7 +105,7 @@ 'Most recent open bugs (PHP 7.3)' => '&bug_type=All&phpver=7.3', 'Open Documentation bugs' => '&bug_type=Documentation+Problem', 'Open Documentation bugs (with patches)' => '&bug_type=Documentation+Problem&patch=Y' - ); + ]; if (!empty($_SESSION["user"])) { $searches['Your assigned open bugs'] = '&assign='.urlencode($_SESSION['user']); diff --git a/www/js/userlisting.php b/www/js/userlisting.php index 1cafd624..139b0bf6 100644 --- a/www/js/userlisting.php +++ b/www/js/userlisting.php @@ -5,8 +5,8 @@ function getAllUsers() { - $opts = array('ignore_errors' => true); - $ctx = stream_context_create(array('http' => $opts)); + $opts = ['ignore_errors' => true]; + $ctx = stream_context_create(['http' => $opts]); $token = getenv('USER_TOKEN'); $retval = @file_get_contents('https://master.php.net/fetch/allusers.php?&token=' . rawurlencode($token), false, $ctx); @@ -47,18 +47,18 @@ function getAllUsers() header("Expires: {$expires}"); } -$lookup = $user = array(); +$lookup = $user = []; if ($json) { foreach ($json as $row) { $lookup[] = $row['name']; $lookup[] = $row["username"]; - $data = array( + $data = [ 'email' => md5($row['username'] . '@php.net'), 'name' => $row['name'], 'username' => $row['username'], - ); + ]; $user[$row["username"]] = $data; $user[$row["name"]] = $data; } diff --git a/www/lstats.php b/www/lstats.php index cf705bc2..b0ccc9a9 100644 --- a/www/lstats.php +++ b/www/lstats.php @@ -27,7 +27,7 @@ function get_status_count ($status, $category = '') } $query.= "AND bug_type NOT IN({$excluded})"; - $res = $dbh->prepare($query)->execute(array()); + $res = $dbh->prepare($query)->execute([]); $row = $res->fetchRow(PDO::FETCH_NUM); return $row[0]; @@ -47,7 +47,7 @@ function get_status_count ($status, $category = '') $project = !empty($_GET['project']) ? $_GET['project'] : false; $pseudo_pkgs = get_pseudo_packages($project); - $totals = array(); + $totals = []; foreach ($pseudo_pkgs as $category => $data) { $count = get_status_count ("status NOT IN('to be documented', 'closed', 'not a bug', 'duplicate', 'wont fix', 'no feedback')", $category); if ($count > 0) { @@ -62,7 +62,7 @@ function get_status_count ($status, $category = '') } else { foreach ($tla as $status => $short) { - if (!in_array($status, array('Duplicate'))) { + if (!in_array($status, ['Duplicate'])) { $count = get_status_count ($status); status_print($status, $count, 30); } diff --git a/www/patch-add.php b/www/patch-add.php index 92bbac1a..368ce701 100644 --- a/www/patch-add.php +++ b/www/patch-add.php @@ -58,7 +58,7 @@ if (isset($_POST['addpatch'])) { if (!isset($_POST['obsoleted'])) { - $_POST['obsoleted'] = array(); + $_POST['obsoleted'] = []; } // Check that patch name is given (required always) @@ -71,7 +71,7 @@ if (!$logged_in) { try { - $errors = array(); + $errors = []; $email = isset($_POST['email']) ? $_POST['email'] : ''; @@ -119,10 +119,10 @@ PEAR::popErrorHandling(); if (PEAR::isError($e)) { $patches = $patchinfo->listPatches($bug_id); - $errors = array($e->getMessage(), + $errors = [$e->getMessage(), 'Could not attach patch "' . htmlspecialchars($patch_name) . - '" to Bug #' . $bug_id); + '" to Bug #' . $bug_id]; include "{$ROOT_DIR}/templates/addpatch.php"; exit; } @@ -144,7 +144,7 @@ mail_bug_updates($buginfo, $buginfo, $auth_user->email, $text, 4, $bug_id); $patches = $patchinfo->listPatches($bug_id); - $errors = array(); + $errors = []; include "{$ROOT_DIR}/templates/patchadded.php"; exit; } diff --git a/www/report.php b/www/report.php index 83f56738..2909d393 100644 --- a/www/report.php +++ b/www/report.php @@ -7,7 +7,7 @@ session_start(); // Init variables -$errors = array(); +$errors = []; $ok_to_submit_report = false; $project = !empty($_GET['project']) ? $_GET['project'] : false; @@ -118,7 +118,7 @@ WHERE bug = ? ORDER BY id DESC LIMIT 1 - ")->execute(array($row['id']))->fetchOne(); + ")->execute([$row['id']])->fetchOne(); $summary = $row['ldesc']; if (strlen($summary) > 256) { @@ -205,7 +205,7 @@ private, visitor_ip ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, "Open", NOW(), ?, INET6_ATON(?)) - ')->execute(array( + ')->execute([ $package_name, $_POST['in']['bug_type'], $_POST['in']['email'], @@ -217,7 +217,7 @@ $_POST['in']['reporter_name'], $_POST['in']['private'], $_SERVER['REMOTE_ADDR'] - ) + ] ); $cid = $dbh->lastInsertId(); @@ -227,7 +227,7 @@ require_once "{$ROOT_DIR}/include/classes/bug_patchtracker.php"; $tracker = new Bug_Patchtracker; PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $patchrevision = $tracker->attach($cid, 'patchfile', $_POST['in']['patchname'], $_POST['in']['handle'], array()); + $patchrevision = $tracker->attach($cid, 'patchfile', $_POST['in']['patchname'], $_POST['in']['handle'], []); PEAR::staticPopErrorHandling(); if (PEAR::isError($patchrevision)) { $redirectToPatchAdd = true; @@ -337,7 +337,7 @@ if (!isset($_POST['in'])) { - $_POST['in'] = array( + $_POST['in'] = [ 'package_name' => isset($_GET['package_name']) ? clean($_GET['package_name']) : '', 'bug_type' => isset($_GET['bug_type']) ? clean($_GET['bug_type']) : '', 'email' => '', @@ -349,7 +349,7 @@ 'php_version' => '', 'php_os' => '', 'passwd' => '', - ); + ]; response_header('Report - New', $packageAffectedScript); diff --git a/www/rpc.php b/www/rpc.php index e63b93c0..82976955 100644 --- a/www/rpc.php +++ b/www/rpc.php @@ -10,7 +10,7 @@ $bug_id = (isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0); if (!$bug_id) { - echo json_encode(array('result' => array('error' => 'Missing bug id'))); + echo json_encode(['result' => ['error' => 'Missing bug id']]); exit; } @@ -23,7 +23,7 @@ $auth_user->handle = $user; $auth_user->password = $pwd; } else { - echo json_encode(array('result' => array('error' => 'Missing credentials'))); + echo json_encode(['result' => ['error' => 'Missing credentials']]); exit; } @@ -32,7 +32,7 @@ $is_trusted_developer = ($user_flags & BUGS_TRUSTED_DEV); if (empty($auth_user->handle)) { - echo json_encode(array('result' => array('error' => 'Invalid user or password'))); + echo json_encode(['result' => ['error' => 'Invalid user or password']]); exit; } @@ -40,12 +40,12 @@ $bug = bugs_get_bug($bug_id); if (!is_array($bug)) { - echo json_encode(array('result' => array('error' => 'No such bug'))); + echo json_encode(['result' => ['error' => 'No such bug']]); exit; } if (!bugs_has_access($bug_id, $bug, $pwd, $user_flags)) { - echo json_encode(array('result' => array('error' => 'No access to bug'))); + echo json_encode(['result' => ['error' => 'No access to bug']]); exit; } @@ -82,15 +82,15 @@ mail_bug_updates($bug, $in, $from, $ncomment, 1, $bug_id); } - echo json_encode(array('result' => array('status' => $bug))); + echo json_encode(['result' => ['status' => $bug]]); exit; } catch (Exception $e) { - echo json_encode(array('result' => array('error' => $e->getMessage()))); + echo json_encode(['result' => ['error' => $e->getMessage()]]); exit; } } else if (!empty($_POST['getbug'])) { - echo json_encode(array('result' => array('status' => $bug))); + echo json_encode(['result' => ['status' => $bug]]); exit; } -echo json_encode(array('result' => array('error' => 'Nothing to do'))); +echo json_encode(['result' => ['error' => 'Nothing to do']]); diff --git a/www/stats.php b/www/stats.php index cc460379..06c1d5f4 100644 --- a/www/stats.php +++ b/www/stats.php @@ -10,7 +10,7 @@ response_header('Bugs Stats'); -$titles = array( +$titles = [ 'Closed' => 'Closed', 'Open' => 'Open', 'Critical' => 'Crit', @@ -23,17 +23,17 @@ 'Not a bug' => 'Not a bug', 'Duplicate' => 'Dupe', 'Wont fix' => 'Wont Fix', -); +]; $rev = isset($_GET['rev']) ? $_GET['rev'] : 1; $sort_by = isset($_GET['sort_by']) ? $_GET['sort_by'] : 'Open'; $total = 0; -$row = array(); -$pkg = array(); -$pkg_tmp = array(); -$pkg_total = array(); -$pkg_names = array(); -$all = array(); +$row = []; +$pkg = []; +$pkg_tmp = []; +$pkg_total = []; +$pkg_names = []; +$all = []; $pseudo = true; $pseudo_pkgs = get_pseudo_packages($site); diff --git a/www/vote.php b/www/vote.php index 0c4b311b..94df5fe0 100644 --- a/www/vote.php +++ b/www/vote.php @@ -20,7 +20,7 @@ $samever = isset($_POST['samever']) ? (int) $_POST['samever'] : 0; $sameos = isset($_POST['sameos']) ? (int) $_POST['sameos'] : 0; -if (!$dbh->prepare("SELECT id FROM bugdb WHERE id= ? LIMIT 1")->execute(array($id))->fetchOne()) { +if (!$dbh->prepare("SELECT id FROM bugdb WHERE id= ? LIMIT 1")->execute([$id])->fetchOne()) { session_start(); // Authenticate @@ -62,7 +62,7 @@ function get_real_ip () // Check whether the user has already voted on this bug. $bug_check = $dbh->prepare("SELECT bug, ip FROM bugdb_votes WHERE bug = ? AND ip = ? LIMIT 1") - ->execute(array($id, $ip)) + ->execute([$id, $ip]) ->fetchRow(); if (empty($bug_check)) { @@ -85,7 +85,7 @@ function get_real_ip () $dbh->prepare("UPDATE bugdb_votes SET score = ?, reproduced = ? , tried = ?, sameos = ?, samever = ? WHERE bug = ? AND ip = ?") - ->execute(array( + ->execute([ $score, ($reproduced == 1 ? "1" : "0"), ($reproduced != 2 ? "1" : "0"), @@ -93,7 +93,7 @@ function get_real_ip () ($reproduced ? "$samever" : null), $id, $ip - )); + ]); // Let the user know they have already voted and the existing vote will be // updated. From 16bea4f84aeb39cda9dc3b647c07c4c4f2749433 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 24 Oct 2018 19:51:47 +0200 Subject: [PATCH 120/277] Refactor SQL files This separates the SQL files according to its functionality: - database.sql for creating only database - schema.sql for creating only database schema - fixtures.sql for inserting only data fixtures This helps in building project from scratch with empty database with only schema to insert custom development fixtures. --- README.md | 4 ++-- sql/{phpbugsdb_create.sql => database.sql} | 0 sql/fixtures.sql | 4 ++++ sql/{bugs.sql => schema.sql} | 4 ---- 4 files changed, 6 insertions(+), 6 deletions(-) rename sql/{phpbugsdb_create.sql => database.sql} (100%) create mode 100644 sql/fixtures.sql rename sql/{bugs.sql => schema.sql} (92%) diff --git a/README.md b/README.md index 97bdf587..20299a85 100644 --- a/README.md +++ b/README.md @@ -31,5 +31,5 @@ pear install --alldeps Text_CAPTCHA_Numeral Text_Diff HTTP_Upload-1.0.0b4 * Database: -Create a new database using `sql/phpbugsdb_create.sql` and insert fixtures using -`sql/bugs.sql`. +Create a new database using `sql/database.sql`, create database schema +`sql/schema.sql` and insert fixtures using `sql/fixtures.sql`. diff --git a/sql/phpbugsdb_create.sql b/sql/database.sql similarity index 100% rename from sql/phpbugsdb_create.sql rename to sql/database.sql diff --git a/sql/fixtures.sql b/sql/fixtures.sql new file mode 100644 index 00000000..c4f3bf79 --- /dev/null +++ b/sql/fixtures.sql @@ -0,0 +1,4 @@ +-- Default pseudo packages (common for all projects) + +INSERT INTO bugdb_pseudo_packages SET id = '1', parent = '0', name = 'Web Site', long_name = 'Web Site', project = ''; +INSERT INTO bugdb_pseudo_packages SET id = '2', parent = '1', name = 'Bug System', long_name = 'Bug System', project = ''; diff --git a/sql/bugs.sql b/sql/schema.sql similarity index 92% rename from sql/bugs.sql rename to sql/schema.sql index 12f0b887..f47075c2 100644 --- a/sql/bugs.sql +++ b/sql/schema.sql @@ -114,7 +114,3 @@ CREATE TABLE bugdb_pulls ( github_html_url varchar(255) NOT NULL, PRIMARY KEY (bugdb_id, github_repo, github_pull_id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; - ---- Default pseudo packages (common for all projects) -INSERT INTO bugdb_pseudo_packages SET id = '1', parent = '0', name = 'Web Site', long_name = 'Web Site', project = ''; -INSERT INTO bugdb_pseudo_packages SET id = '2', parent = '1', name = 'Bug System', long_name = 'Bug System', project = ''; From cd01d60c3f2ee72fd22f8f0e31da6b5cb7b13ae1 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 24 Oct 2018 20:25:16 +0200 Subject: [PATCH 121/277] Remove redundant script type="text/javascript" attributes Omitted type means that script is JavaScript [1] and for HTML 5 browsers can be removed today. [1] https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script --- templates/addghpull.php | 6 +++--- www/bug.php | 14 +++++++------- www/index.php | 2 +- www/report.php | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/templates/addghpull.php b/templates/addghpull.php index ea9dc264..a267c6cd 100644 --- a/templates/addghpull.php +++ b/templates/addghpull.php @@ -1,9 +1,9 @@ - - - + + +

    Add a Pull Request to Bug #

    • One problem per pull request, please
    • diff --git a/www/bug.php b/www/bug.php index a089041d..26183b10 100644 --- a/www/bug.php +++ b/www/bug.php @@ -1149,10 +1149,10 @@ - - - + + + - - + + + '; } diff --git a/www/index.php b/www/index.php index a43a38a3..c7e29dc6 100644 --- a/www/index.php +++ b/www/index.php @@ -28,7 +28,7 @@ ?> - + SCRIPT; // Handle input From 6ca97626c02e8918960395d33a8344b73796035d Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Thu, 25 Oct 2018 03:21:12 +0200 Subject: [PATCH 122/277] Fix extra indentation --- include/php_versions.php | 181 +++++++++++++++++++-------------------- 1 file changed, 90 insertions(+), 91 deletions(-) diff --git a/include/php_versions.php b/include/php_versions.php index 88600497..8aa07af3 100644 --- a/include/php_versions.php +++ b/include/php_versions.php @@ -1,107 +1,106 @@ /tmp/versions.php - the versions are weighted by the following: - - major+minor version desc (7>5.4>5.3>master) - - between a minor version we order by the micro if available: first the qa releases: alpha/beta/rc, then the stable, then the Git versions(snaps, Git) - - Stable releases are pulled from https://php.net/releases/active.php - */ - - // Custom versions appended to the list - $custom_versions = [ - 'Next Major Version', - 'Next Minor Version', - 'Irrelevant' +/* +The RC and dev versions are pulled from the https://qa.php.net/api.php +if you want to add a new version, add it there at include/release-qa.php +the result is cached for an hour in /tmp//tmp/versions.php +the versions are weighted by the following: +- major+minor version desc (7>5.4>5.3>master) +- between a minor version we order by the micro if available: first the qa releases: alpha/beta/rc, then the stable, then the Git versions(snaps, Git) + +Stable releases are pulled from https://php.net/releases/active.php +*/ + +// Custom versions appended to the list +$custom_versions = [ + 'Next Major Version', + 'Next Minor Version', + 'Irrelevant' +]; + +if(!file_exists("/tmp/versions.php") || filemtime("/tmp/versions.php") < $_SERVER['REQUEST_TIME'] - 3600) { + $versions = buildVersions(); + $versions_data = var_export($versions, true); + file_put_contents("/tmp/versions.php", ' $major_releases) { - foreach ($major_releases as $release) { - $version_parts = parseVersion($release['version']); - $versions[$version_parts['major']][$version_parts['minor']][$version_parts['micro']] = $version_parts; - ksort($versions[$version_parts['major']][$version_parts['minor']]); - } + $stable_releases = json_decode(file_get_contents('https://php.net/releases/active.php'), true); + foreach ($stable_releases as $major => $major_releases) { + foreach ($major_releases as $release) { + $version_parts = parseVersion($release['version']); + $versions[$version_parts['major']][$version_parts['minor']][$version_parts['micro']] = $version_parts; + ksort($versions[$version_parts['major']][$version_parts['minor']]); } + } - $flat_versions = []; + $flat_versions = []; - // add master to the end of the list - foreach ($default_versions as $default_version) { - $flat_versions[] = 'master-'.$default_version; - } + // add master to the end of the list + foreach ($default_versions as $default_version) { + $flat_versions[] = 'master-'.$default_version; + } - // add the fetched versions to the list - foreach ($versions as $major_number => $major) { - foreach ($major as $minor_number => $minor) { - // add the default versions to ever minor branch - foreach ($default_versions as $default_version) { - $flat_versions[] = $major_number.'.'.$minor_number.$default_version; - } - foreach ($minor as $micro_number => $micro) { - $flat_versions[] = $micro['original_version']; - } + // add the fetched versions to the list + foreach ($versions as $major_number => $major) { + foreach ($major as $minor_number => $minor) { + // add the default versions to ever minor branch + foreach ($default_versions as $default_version) { + $flat_versions[] = $major_number.'.'.$minor_number.$default_version; + } + foreach ($minor as $micro_number => $micro) { + $flat_versions[] = $micro['original_version']; } } - - // reverse the order, this makes it descending - $flat_versions = array_reverse($flat_versions); - - return $flat_versions; } - - function parseVersion($version){ - $version_parts = []; - $raw_parts = []; - preg_match('#(?P\d+)\.(?P\d+).(?P\d+)[-]?(?PRC|alpha|beta|dev)?(?P[\d]?).*#ui', $version, $raw_parts); - $version_parts = [ - 'major' => $raw_parts['major'], - 'minor' => $raw_parts['minor'], - 'micro' => $raw_parts['micro'], - 'type' => strtolower($raw_parts['type']?$raw_parts['type']:'stable'), - 'number' => $raw_parts['number'], - 'original_version' => $version, - ]; - return $version_parts; - } + // reverse the order, this makes it descending + $flat_versions = array_reverse($flat_versions); + + return $flat_versions; +} + +function parseVersion($version){ + $version_parts = []; + $raw_parts = []; + preg_match('#(?P\d+)\.(?P\d+).(?P\d+)[-]?(?PRC|alpha|beta|dev)?(?P[\d]?).*#ui', $version, $raw_parts); + $version_parts = [ + 'major' => $raw_parts['major'], + 'minor' => $raw_parts['minor'], + 'micro' => $raw_parts['micro'], + 'type' => strtolower($raw_parts['type']?$raw_parts['type']:'stable'), + 'number' => $raw_parts['number'], + 'original_version' => $version, + ]; + return $version_parts; +} From 6f59aec8dc64e2129083805144bdd70b56998085 Mon Sep 17 00:00:00 2001 From: Dan Wallis Date: Sun, 4 Nov 2018 18:49:41 +0000 Subject: [PATCH 123/277] Fix typo in Add GitHub Pull Request template --- templates/addghpull.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/addghpull.php b/templates/addghpull.php index a267c6cd..9884a4ad 100644 --- a/templates/addghpull.php +++ b/templates/addghpull.php @@ -7,7 +7,7 @@

      Add a Pull Request to Bug #

      • One problem per pull request, please
      • -
      • The pull requst must be opened against a PHP project on GitHub
      • +
      • The pull request must be opened against a PHP project on GitHub
      • Choose a meaningful request name (i.e. include bug id and title)
      From 13ecc0245ad34f8af7e955064bc7d6c2f6240c17 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Tue, 13 Nov 2018 15:00:03 +0100 Subject: [PATCH 124/277] Add note regarding active vs. security support Cf. . --- www/how-to-report.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/www/how-to-report.php b/www/how-to-report.php index 7d1769b1..a0a6b030 100644 --- a/www/how-to-report.php +++ b/www/how-to-report.php @@ -106,11 +106,17 @@

      Don't report bugs about old versions.

      -

      Every time a new version of PHP is released, hundreds of bugs are fixed. If +

      Every time a new version of PHP is released, dozens of bugs are fixed. If you're using a version of PHP that is more than two revisions older than the latest version, you should upgrade to the latest version to make sure the bug you are experiencing still exists.

      +

      Note that PHP branches which are no longer actively supported will +receive fixes for critical security issues only. So please do not report +non-security related bugs which do not affect any actively supported PHP +branch.

      +

      Only report one problem in each bug report.

      If you have encountered two bugs that don't appear to be related, create a From 0818111ce98a81dcf40ec23c7707ae7804443567 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sat, 24 Nov 2018 02:10:34 +0100 Subject: [PATCH 125/277] Bump minimal required PHP version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 20299a85..555a497f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This is a unified bug tracking system for PHP hosted online at ## Requirements -- PHP 5.4+ +- PHP 7.2+ - ext/pdo - ext/pdo_mysql - ext/openssl (for https:// fopen wrapper) From 38212625bd3c4356c1173c0ef4fd3af76f404fb5 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sat, 24 Nov 2018 02:25:23 +0100 Subject: [PATCH 126/277] Refactor each() function to foreach() The each() function has been deprecated since PHP 7.2 and shouldn't be used anymore: - http://php.net/manual/en/function.each.php --- include/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/functions.php b/include/functions.php index 13b9c649..cb6d6aa3 100644 --- a/include/functions.php +++ b/include/functions.php @@ -498,7 +498,7 @@ function show_byage_options($current) '30' => '30 days ago', '90' => '90 days ago', ]; - while (list($k,$v) = each($opts)) { + foreach ($opts as $k => $v) { echo "\n"; } } @@ -784,7 +784,7 @@ function ($value) { function show_boolean_options($current) { $options = ['any', 'all', 'raw']; - while (list($val, $type) = each($options)) { + foreach ($options as $val => $type) { echo ' Date: Sun, 2 Dec 2018 21:56:06 +0100 Subject: [PATCH 127/277] Add uploads directory to .gitignore --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 62c5408e..e10d4016 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -local_config.php +# Uploaded patches +/uploads/ + +# Configuration file +/local_config.php From 017e87d850745e11b68228f1a11e5bb46543494e Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 3 Dec 2018 00:02:21 +0100 Subject: [PATCH 128/277] Add methods and properties visibility --- include/classes/bug_diff_renderer.php | 18 +++---- include/classes/bug_ghpulltracker.php | 19 ++++--- include/classes/bug_patchtracker.php | 73 +++++++++++++-------------- 3 files changed, 54 insertions(+), 56 deletions(-) diff --git a/include/classes/bug_diff_renderer.php b/include/classes/bug_diff_renderer.php index aec68092..9fb0b3c1 100644 --- a/include/classes/bug_diff_renderer.php +++ b/include/classes/bug_diff_renderer.php @@ -16,18 +16,18 @@ class Bug_Diff_Renderer extends Text_Diff_Renderer { // Number of leading context "lines" to preserve. - var $_leading_context_lines = 4; + public $_leading_context_lines = 4; // Number of trailing context "lines" to preserve. - var $_trailing_context_lines = 4; + public $_trailing_context_lines = 4; - function __construct($d) + public function __construct($d) { $this->diff = $d; parent::__construct(); } - function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + public function _blockHeader($xbeg, $xlen, $ybeg, $ylen) { $removed = $xlen - $ylen; if ($removed > 0) { @@ -35,30 +35,30 @@ function _blockHeader($xbeg, $xlen, $ybeg, $ylen) } } - function _added($lines) + public function _added($lines) { array_walk($lines, create_function('&$a,$b', '$a=htmlspecialchars($a);')); return ' ' . implode("\n ", $lines) . ''; } - function _context($lines) + public function _context($lines) { array_walk($lines, create_function('&$a,$b', '$a=htmlspecialchars($a);')); return "\n" . parent::_context($lines); } - function _deleted($lines) + public function _deleted($lines) { array_walk($lines, create_function('&$a,$b', '$a=htmlspecialchars($a);')); return ' ' . implode("\n ", $lines) . ''; } - function _changed($orig, $final) + public function _changed($orig, $final) { return $this->_deleted($orig) . "\n" . $this->_added($final); } - function render($diff) + public function render($diff) { return parent::render($this->diff); } diff --git a/include/classes/bug_ghpulltracker.php b/include/classes/bug_ghpulltracker.php index 3ad968ce..5e7cbee6 100644 --- a/include/classes/bug_ghpulltracker.php +++ b/include/classes/bug_ghpulltracker.php @@ -2,13 +2,12 @@ class Bug_Pulltracker { - var $_dbh; + private $dbh; private $userAgent = 'bugs.php.net Pulltracker'; - - function __construct() + public function __construct() { - $this->_dbh = $GLOBALS['dbh']; + $this->dbh = $GLOBALS['dbh']; } private function getDataFromGithub($repo, $pull_id) @@ -29,14 +28,14 @@ private function getDataFromGithub($repo, $pull_id) /** * Attach a pull request to this bug */ - function attach($bugid, $repo, $pull_id, $developer) + public function attach($bugid, $repo, $pull_id, $developer) { $data = $this->getDataFromGithub($repo, $pull_id); if (!$data) { return PEAR::raiseError('Failed to retrieve pull request from GitHub'); } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $e = $this->_dbh->prepare('INSERT INTO bugdb_pulls + $e = $this->dbh->prepare('INSERT INTO bugdb_pulls (bugdb_id, github_repo, github_pull_id, github_title, github_html_url, developer) VALUES (?, ?, ?, ?, ?, ?)')->execute( [$bugid, $repo, $pull_id, $data->title, $data->html_url, $developer]); PEAR::popErrorHandling(); @@ -50,9 +49,9 @@ function attach($bugid, $repo, $pull_id, $developer) /** * Remove a pull request from this bug */ - function detach($bugid, $repo, $pull_id) + public function detach($bugid, $repo, $pull_id) { - $this->_dbh->prepare('DELETE FROM bugdb_pulls + $this->dbh->prepare('DELETE FROM bugdb_pulls WHERE bugdb_id = ? and github_repo = ? and github_pull_id = ?')->execute( [$bugid, $repo, $pull_id]); } @@ -63,7 +62,7 @@ function detach($bugid, $repo, $pull_id) * @param int $bugid * @return array */ - function listPulls($bugid) + public function listPulls($bugid) { $query = ' SELECT github_repo, github_pull_id, github_title, github_html_url, developer @@ -72,6 +71,6 @@ function listPulls($bugid) ORDER BY github_repo, github_pull_id DESC '; - return $this->_dbh->prepare($query)->execute([$bugid])->fetchAll(PDO::FETCH_ASSOC); + return $this->dbh->prepare($query)->execute([$bugid])->fetchAll(PDO::FETCH_ASSOC); } } diff --git a/include/classes/bug_patchtracker.php b/include/classes/bug_patchtracker.php index a66f49ad..2ec645cb 100644 --- a/include/classes/bug_patchtracker.php +++ b/include/classes/bug_patchtracker.php @@ -4,21 +4,20 @@ class Bug_Patchtracker { - var $_upload; - var $_patchdir; - var $_dbh; + private $uploader; + private $dbh; - function __construct() + public function __construct() { if (!file_exists(BUG_PATCHTRACKER_TMPDIR)) { if (!@mkdir(BUG_PATCHTRACKER_TMPDIR)) { - $this->_upload = false; - $this->_dbh = $GLOBALS['dbh']; + $this->uploader = false; + $this->dbh = $GLOBALS['dbh']; return; } } - $this->_upload = new HTTP_Upload('en'); - $this->_dbh = $GLOBALS['dbh']; + $this->uploader = new HTTP_Upload('en'); + $this->dbh = $GLOBALS['dbh']; } /** @@ -28,7 +27,7 @@ function __construct() * @param string $name name of this patch line * @return string */ - function patchDir($bugid, $name) + private function patchDir($bugid, $name) { return BUG_PATCHTRACKER_TMPDIR . '/p' . $bugid . '/' . $name; } @@ -37,7 +36,7 @@ function patchDir($bugid, $name) * * @param int $bugid */ - function setupPatchDir($bugid, $name) + private function setupPatchDir($bugid, $name) { if (file_exists($this->patchDir($bugid, $name))) { if (!is_dir($this->patchDir($bugid, $name))) { @@ -70,17 +69,17 @@ function setupPatchDir($bugid, $name) * @param string $patch * @return array array(revision, patch file name) */ - function newPatchFileName($bugid, $patch, $handle) + private function newPatchFileName($bugid, $patch, $handle) { $id = time(); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $e = $this->_dbh->prepare('INSERT INTO bugdb_patchtracker + $e = $this->dbh->prepare('INSERT INTO bugdb_patchtracker (bugdb_id, patch, revision, developer) VALUES(?, ?, ?, ?)')->execute( [$bugid, $patch, $id, $handle]); if (PEAR::isError($e)) { // try with another timestamp $id++; - $e = $this->_dbh->prepare('INSERT INTO bugdb_patchtracker + $e = $this->dbh->prepare('INSERT INTO bugdb_patchtracker (bugdb_id, patch, revision, developer) VALUES(?, ?, ?, ?)')->execute( [$bugid, $patch, $id, $handle]); } @@ -97,7 +96,7 @@ function newPatchFileName($bugid, $patch, $handle) * @param int $id revision ID * @return string */ - function getPatchFileName($id) + private function getPatchFileName($id) { return 'p' . $id . '.patch.txt'; } @@ -110,7 +109,7 @@ function getPatchFileName($id) * @param int $revision * @return string */ - function getPatchFullpath($bugid, $name, $revision) + public function getPatchFullpath($bugid, $name, $revision) { return $this->patchDir($bugid, $name) . DIRECTORY_SEPARATOR . $this->getPatchFileName($revision); @@ -126,9 +125,9 @@ function getPatchFullpath($bugid, $name, $revision) * @param array $obsoletes obsoleted patches * @return int patch revision */ - function attach($bugid, $patch, $name, $handle, $obsoletes) + public function attach($bugid, $patch, $name, $handle, $obsoletes) { - if (!$this->_upload) { + if (!$this->uploader) { return PEAR::raiseError('Upload directory for patches could not be initialized'); } if (!preg_match('/^[\w\-\.]+\z/', $name) || strlen($name) > 80) { @@ -138,7 +137,7 @@ function attach($bugid, $patch, $name, $handle, $obsoletes) return PEAR::raiseError('Invalid obsoleted patches'); } - $file = $this->_upload->getFiles($patch); + $file = $this->uploader->getFiles($patch); if (PEAR::isError($file)) { return $file; } @@ -195,7 +194,7 @@ function attach($bugid, $patch, $name, $handle, $obsoletes) $mime = 'text/plain'; } if (!in_array($mime, $allowed_mime_types)) { - $this->_dbh->prepare('DELETE FROM bugdb_patchtracker + $this->dbh->prepare('DELETE FROM bugdb_patchtracker WHERE bugdb_id = ? and patch = ? and revision = ?')->execute( [$bugid, $name, $id]); return PEAR::raiseError('Error: uploaded patch file must be text' @@ -205,7 +204,7 @@ function attach($bugid, $patch, $name, $handle, $obsoletes) } $tmpfile = $file->moveTo($this->patchDir($bugid, $name)); if (PEAR::isError($tmpfile)) { - $this->_dbh->prepare('DELETE FROM bugdb_patchtracker + $this->dbh->prepare('DELETE FROM bugdb_patchtracker WHERE bugdb_id = ? and patch = ? and revision = ?')->execute( [$bugid, $name, $id]); return $tmpfile; @@ -237,9 +236,9 @@ function attach($bugid, $patch, $name, $handle, $obsoletes) * @param string $name * @param int $revision */ - function detach($bugid, $name, $revision) + public function detach($bugid, $name, $revision) { - $this->_dbh->prepare('DELETE FROM bugdb_patchtracker + $this->dbh->prepare('DELETE FROM bugdb_patchtracker WHERE bugdb_id = ? and patch = ? and revision = ?')->execute( [$bugid, $name, $revision]); @unlink($this->patchDir($bugid, $name) . DIRECTORY_SEPARATOR . @@ -254,9 +253,9 @@ function detach($bugid, $name, $revision) * @param int $revision * @return string */ - function getPatch($bugid, $name, $revision) + public function getPatch($bugid, $name, $revision) { - if ($this->_dbh->prepare(' + if ($this->dbh->prepare(' SELECT bugdb_id FROM bugdb_patchtracker WHERE bugdb_id = ? AND patch = ? AND revision = ?')->execute([$bugid, $name, $revision])->fetchOne() @@ -276,7 +275,7 @@ function getPatch($bugid, $name, $revision) * @param int $bugid * @return array */ - function listPatches($bugid) + public function listPatches($bugid) { $query = ' SELECT patch, revision, developer @@ -285,7 +284,7 @@ function listPatches($bugid) ORDER BY revision DESC '; - return $this->_dbh->prepare($query)->execute([$bugid])->fetchAll(PDO::FETCH_NUM, true, false, true); + return $this->dbh->prepare($query)->execute([$bugid])->fetchAll(PDO::FETCH_NUM, true, false, true); } /** @@ -295,14 +294,14 @@ function listPatches($bugid) * @param string $patch * @return array */ - function listRevisions($bugid, $patch) + public function listRevisions($bugid, $patch) { $query = ' SELECT revision FROM bugdb_patchtracker WHERE bugdb_id = ? AND patch = ? ORDER BY revision DESC '; - return $this->_dbh->prepare($query)->execute([$bugid, $patch])->fetchAll(PDO::FETCH_NUM); + return $this->dbh->prepare($query)->execute([$bugid, $patch])->fetchAll(PDO::FETCH_NUM); } /** @@ -313,34 +312,34 @@ function listRevisions($bugid, $patch) * @param int $revision * @return string|array array if no revision is selected */ - function getDeveloper($bugid, $patch, $revision = false) + public function getDeveloper($bugid, $patch, $revision = false) { if ($revision) { - return $this->_dbh->prepare(' + return $this->dbh->prepare(' SELECT developer FROM bugdb_patchtracker WHERE bugdb_id = ? AND patch = ? AND revision = ? ')->execute([$bugid, $patch, $revision])->fetchOne(); } - return $this->_dbh->prepare(' + return $this->dbh->prepare(' SELECT developer, revision FROM bugdb_patchtracker WHERE bugdb_id = ? AND patch = ? ORDER BY revision DESC ')->execute([$bugid, $patch])->fetchAll(PDO::FETCH_ASSOC); } - function getObsoletingPatches($bugid, $patch, $revision) + public function getObsoletingPatches($bugid, $patch, $revision) { - return $this->_dbh->prepare(' + return $this->dbh->prepare(' SELECT bugdb_id, patch, revision FROM bugdb_obsoletes_patches WHERE bugdb_id = ? AND obsolete_patch = ? AND obsolete_revision = ? ')->execute([$bugid, $patch, $revision])->fetchAll(PDO::FETCH_ASSOC); } - function getObsoletePatches($bugid, $patch, $revision) + public function getObsoletePatches($bugid, $patch, $revision) { - return $this->_dbh->prepare(' + return $this->dbh->prepare(' SELECT bugdb_id, obsolete_patch, obsolete_revision FROM bugdb_obsoletes_patches WHERE bugdb_id = ? AND patch = ? AND revision = ? @@ -356,9 +355,9 @@ function getObsoletePatches($bugid, $patch, $revision) * @param string $obsoletename * @param int $obsoleterevision */ - function obsoletePatch($bugid, $name, $revision, $obsoletename, $obsoleterevision) + private function obsoletePatch($bugid, $name, $revision, $obsoletename, $obsoleterevision) { - $this->_dbh->prepare(' + $this->dbh->prepare(' INSERT INTO bugdb_obsoletes_patches VALUES(?, ?, ?, ?, ?) ')->execute([$bugid, $name, $revision, $obsoletename, $obsoleterevision]); From 32466d710928c42d841e519a61cd8743c26cfea4 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 3 Dec 2018 00:04:33 +0100 Subject: [PATCH 129/277] Replace DIRECTORY_SEPARATOR with slash string The constant DIRECTORY_SEPARATOR is in the used cases not needed to work ok on Windows. PHP functions in these cases can handle normal slash fine and this patch simplifies this a bit. --- include/classes/bug_patchtracker.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/classes/bug_patchtracker.php b/include/classes/bug_patchtracker.php index 2ec645cb..18c01d29 100644 --- a/include/classes/bug_patchtracker.php +++ b/include/classes/bug_patchtracker.php @@ -111,8 +111,7 @@ private function getPatchFileName($id) */ public function getPatchFullpath($bugid, $name, $revision) { - return $this->patchDir($bugid, $name) . - DIRECTORY_SEPARATOR . $this->getPatchFileName($revision); + return $this->patchDir($bugid, $name).'/'.$this->getPatchFileName($revision); } /** @@ -241,8 +240,8 @@ public function detach($bugid, $name, $revision) $this->dbh->prepare('DELETE FROM bugdb_patchtracker WHERE bugdb_id = ? and patch = ? and revision = ?')->execute( [$bugid, $name, $revision]); - @unlink($this->patchDir($bugid, $name) . DIRECTORY_SEPARATOR . - $this->getPatchFileName($revision)); + + @unlink($this->patchDir($bugid, $name).'/'.$this->getPatchFileName($revision)); } /** From 83d6860af9a4554a234177d1d38f37907fcb4b4f Mon Sep 17 00:00:00 2001 From: Niklas Keller Date: Mon, 3 Dec 2018 01:02:16 +0100 Subject: [PATCH 130/277] Add Composer This patch adds initial Composer files and prepares the project for using Composer in development environment. Patch has been initially started via pull request at https://github.com/php/web-bugs/pull/27 --- .gitignore | 3 +++ README.md | 17 ++++++----------- composer.json | 31 +++++++++++++++++++++++++++++++ composer.lock | 24 ++++++++++++++++++++++++ include/prepend.php | 4 ++++ 5 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 composer.json create mode 100644 composer.lock diff --git a/.gitignore b/.gitignore index e10d4016..56b323fc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ # Configuration file /local_config.php + +# Generated by Composer +/vendor/ diff --git a/README.md b/README.md index 555a497f..51707963 100644 --- a/README.md +++ b/README.md @@ -3,17 +3,6 @@ This is a unified bug tracking system for PHP hosted online at [bugs.php.net](https://bugs.php.net). -## Requirements - -- PHP 7.2+ -- ext/pdo -- ext/pdo_mysql -- ext/openssl (for https:// fopen wrapper) -- PEAR packages: - - Text_CAPTCHA_Numeral - - Text_Diff - - HTTP_Upload (1.0.0b4 or later) - ## Local installation * Copy configuration and modify it accordingly for your local system: @@ -22,6 +11,12 @@ This is a unified bug tracking system for PHP hosted online at cp local_config.php.sample local_config.php ``` +* Install development dependencies with Composer: + +```bash +composer install +``` + * Install required dependencies using PEAR: ```bash diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..30638510 --- /dev/null +++ b/composer.json @@ -0,0 +1,31 @@ +{ + "name": "php/web-bugs", + "description": "The PHP Bugtracking System", + "type": "project", + "keywords": ["bugs", "php", "website"], + "homepage": "https://bugs.php.net/", + "readme": "README.md", + "license": "PHP license", + "authors": [ + { + "name": "PHP contributors" + } + ], + "support": { + "email": "php-webmaster@lists.php.net", + "issues": "https://bugs.php.net/", + "wiki": "https://wiki.php.net/", + "irc": "irc://irc.efnet.org/php.pecl", + "source": "https://git.php.net/?p=web/bugs.git", + "docs": "https://php.net/manual", + "rss": "https://bugs.php.net/rss" + }, + "require": { + "php": "^7.2", + "ext-json": "*", + "ext-openssl": "*", + "ext-pdo": "*", + "ext-pdo_mysql": "*", + "ext-session": "*" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000..44056578 --- /dev/null +++ b/composer.lock @@ -0,0 +1,24 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "ec56b1b2037ec16e6e8fd80f4c1b8f1a", + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^7.2", + "ext-json": "*", + "ext-openssl": "*", + "ext-pdo": "*", + "ext-pdo_mysql": "*", + "ext-session": "*" + }, + "platform-dev": [] +} diff --git a/include/prepend.php b/include/prepend.php index 367510ff..84176079 100644 --- a/include/prepend.php +++ b/include/prepend.php @@ -1,5 +1,9 @@ Date: Mon, 3 Dec 2018 03:08:45 +0100 Subject: [PATCH 131/277] Refactor numerical Captcha package into class The Text_CAPTCHA_Numeral can be integrated in the project source code instead of using a separate unmaintained dependency. This patch also adds a simple unit test. --- .gitignore | 3 + README.md | 11 +- composer.json | 13 + composer.lock | 1413 ++++++++++++++++++++++++++++++++++- phpunit.xml.dist | 20 + src/Utils/Captcha.php | 138 ++++ templates/addghpull.php | 5 +- templates/addpatch.php | 5 +- tests/Utils/CaptchaTest.php | 41 + www/bug-pwd-finder.php | 12 +- www/bug.php | 11 +- www/gh-pull-add.php | 6 +- www/patch-add.php | 6 +- www/report.php | 12 +- 14 files changed, 1668 insertions(+), 28 deletions(-) create mode 100644 phpunit.xml.dist create mode 100644 src/Utils/Captcha.php create mode 100644 tests/Utils/CaptchaTest.php diff --git a/.gitignore b/.gitignore index 56b323fc..6c2d0110 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,8 @@ # Configuration file /local_config.php +# Local specific PHPUnit configuration +/phpunit.xml + # Generated by Composer /vendor/ diff --git a/README.md b/README.md index 51707963..564aa476 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,19 @@ composer install ```bash pear channel-update pear.php.net -pear install --alldeps Text_CAPTCHA_Numeral Text_Diff HTTP_Upload-1.0.0b4 +pear install --alldeps Text_Diff HTTP_Upload-1.0.0b4 ``` * Database: Create a new database using `sql/database.sql`, create database schema `sql/schema.sql` and insert fixtures using `sql/fixtures.sql`. + +## Tests + +Application unit tests can be executed in development environment after +installing dependencies by running `phpunit`: + +```bash +phpunit +``` diff --git a/composer.json b/composer.json index 30638510..ddb3fa37 100644 --- a/composer.json +++ b/composer.json @@ -27,5 +27,18 @@ "ext-pdo": "*", "ext-pdo_mysql": "*", "ext-session": "*" + }, + "require-dev": { + "phpunit/phpunit": "^7.4" + }, + "autoload": { + "psr-4": { + "App\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "App\\Tests\\": "tests/" + } } } diff --git a/composer.lock b/composer.lock index 44056578..b0475dab 100644 --- a/composer.lock +++ b/composer.lock @@ -4,9 +4,1418 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ec56b1b2037ec16e6e8fd80f4c1b8f1a", + "content-hash": "bb74d235e97b99c5049cfbbdbb6a3654", "packages": [], - "packages-dev": [], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "^6.2.3", + "squizlabs/php_codesniffer": "^3.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2017-07-22T11:58:36+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2018-06-11T23:09:50+00:00" + }, + { + "name": "phar-io/manifest", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^2.0", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2018-07-08T19:23:20+00:00" + }, + { + "name": "phar-io/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2018-07-08T19:19:57+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org/", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2017-09-11T18:02:19+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "4.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08", + "shasum": "" + }, + "require": { + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "doctrine/instantiator": "~1.0.5", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2017-11-30T07:14:17+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2017-07-14T14:27:02+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com/" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2018-08-05T17:53:17+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "6.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.1", + "phpunit/php-file-iterator": "^2.0", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^3.0", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^3.1 || ^4.0", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "suggest": { + "ext-xdebug": "^2.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2018-10-31T16:06:48+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "050bedf145a257b1ff02746c31894800e5122946" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", + "reference": "050bedf145a257b1ff02746c31894800e5122946", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2018-09-13T20:33:42+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f", + "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2018-02-01T13:07:23+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18", + "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2018-10-30T05:52:18+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "7.4.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "b1be2c8530c4c29c3519a052c9fb6cee55053bbd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b1be2c8530c4c29c3519a052c9fb6cee55053bbd", + "reference": "b1be2c8530c4c29c3519a052c9fb6cee55053bbd", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", + "php": "^7.1", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^6.0.7", + "phpunit/php-file-iterator": "^2.0.1", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^2.0", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", + "sebastian/environment": "^3.1 || ^4.0", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^2.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^2.0", + "sebastian/version": "^2.0.1" + }, + "conflict": { + "phpunit/phpunit-mock-objects": "*" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*", + "phpunit/php-invoker": "^2.0" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2018-11-14T16:52:02+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04T06:30:41+00:00" + }, + { + "name": "sebastian/comparator", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "shasum": "" + }, + "require": { + "php": "^7.1", + "sebastian/diff": "^3.0", + "sebastian/exporter": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2018-07-12T15:12:46+00:00" + }, + { + "name": "sebastian/diff", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "366541b989927187c4ca70490a35615d3fef2dce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", + "reference": "366541b989927187c4ca70490a35615d3fef2dce", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0", + "symfony/process": "^2 || ^3.3 || ^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "time": "2018-06-10T07:54:39+00:00" + }, + { + "name": "sebastian/environment", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "febd209a219cea7b56ad799b30ebbea34b71eb8f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/febd209a219cea7b56ad799b30ebbea34b71eb8f", + "reference": "febd209a219cea7b56ad799b30ebbea34b71eb8f", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2018-11-25T09:31:21+00:00" + }, + { + "name": "sebastian/exporter", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2017-04-03T13:19:02+00:00" + }, + { + "name": "sebastian/global-state", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2017-04-27T15:39:26+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-08-03T12:35:26+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "773f97c67f28de00d397be301821b06708fca0be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2017-03-29T09:07:27+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2017-03-03T06:23:57+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2018-10-04T04:07:39+00:00" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2017-04-07T12:08:54+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2018-01-29T19:49:41+00:00" + } + ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 00000000..a793290a --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + + + + + + + + tests/ + + + diff --git a/src/Utils/Captcha.php b/src/Utils/Captcha.php new file mode 100644 index 00000000..01377aaf --- /dev/null +++ b/src/Utils/Captcha.php @@ -0,0 +1,138 @@ + 'addition', + '-' => 'subtraction', + ]; + + /** + * Current operation. + */ + private $operation; + + /** + * Class constructor where operands random values and operation are set. + */ + public function __construct() + { + $this->randomize(); + } + + /** + * Set random operands values and operation. + */ + public function randomize() + { + $this->setFirst(rand(1, self::MAX)); + $this->setLast(rand(1, self::MAX)); + $this->setOperation(self::OPERATIONS[array_rand(self::OPERATIONS)]); + } + + /** + * First operand number setter to override default random pick. Defined as a + * separate method for convenience when unit testing. + */ + public function setFirst($number) + { + $this->first = $number; + } + + /** + * Last operand number setter to override default random pick. Defined as a + * separate method for convenience when unit testing. + */ + public function setLast($number) + { + $this->last = $number; + } + + /** + * Set the operation. If provided operation is invalid it falls back to addition. + */ + public function setOperation($operation) + { + $this->operation = in_array($operation, self::OPERATIONS) ? $operation : 'addition'; + } + + /** + * Get current question equation string for displaying it to the user. + */ + public function getQuestion() + { + $this->sortOperands(); + + $symbol = array_search($this->operation, self::OPERATIONS); + $symbol = $symbol === false ? '+' : $symbol; + + return $this->first.' '.$symbol.' '.$this->last.' = ?'; + } + + /** + * The correct current answer of the given equation question. + */ + public function getAnswer() + { + $this->sortOperands(); + + return \call_user_func([Captcha::class, $this->operation], $this->first, $this->last); + } + + /** + * When the current operation is subtraction, sort operands to have a bigger + * operand first. With this, negative results are omitted for simplicity and + * possible better user experience. + */ + private function sortOperands() + { + $first = $this->first; + $last = $this->last; + + if ($this->operation === 'subtraction') { + $this->first = $first > $last ? $first : $last; + $this->last = $first > $last ? $last : $first; + } + } + + /** + * Addition of two operands. + */ + private function addition($first, $last) + { + return $first + $last; + } + + /** + * Subtraction of two operands. + */ + private function subtraction($first, $last) + { + return $first - $last; + } +} diff --git a/templates/addghpull.php b/templates/addghpull.php index 9884a4ad..fb06dd13 100644 --- a/templates/addghpull.php +++ b/templates/addghpull.php @@ -23,8 +23,7 @@ getOperation(); - $_SESSION['answer'] = $numeralCaptcha->getAnswer(); + $_SESSION['answer'] = $captcha->getAnswer(); ?> - + diff --git a/templates/addpatch.php b/templates/addpatch.php index 6e0a7f6c..52857890 100644 --- a/templates/addpatch.php +++ b/templates/addpatch.php @@ -21,8 +21,7 @@
      @@ -35,7 +34,7 @@
      Solve the problem:
      = ?
      Solve the problem:
      getQuestion(); ?>
      getOperation(); - $_SESSION['answer'] = $numeralCaptcha->getAnswer(); + $_SESSION['answer'] = $captcha->getAnswer(); ?> - + diff --git a/tests/Utils/CaptchaTest.php b/tests/Utils/CaptchaTest.php new file mode 100644 index 00000000..681da7a4 --- /dev/null +++ b/tests/Utils/CaptchaTest.php @@ -0,0 +1,41 @@ +captcha = new Captcha(); + } + + /** + * @dataProvider mathProvider + */ + public function testGetQuestion($first, $last, $operation, $question, $expected) + { + $this->captcha->setFirst($first); + $this->captcha->setLast($last); + $this->captcha->setOperation($operation); + + $this->assertEquals($question, $this->captcha->getQuestion()); + $this->assertEquals($expected, $this->captcha->getAnswer()); + } + + public function mathProvider() + { + return [ + [1, 2, 'addition', '1 + 2 = ?', 3], + [10, 50, 'subtraction', '50 - 10 = ?', 40], + [90, 50, 'subtraction', '90 - 50 = ?', 40], + [14, 14, 'subtraction', '14 - 14 = ?', 0], + [10, 5, 'multiplication', '10 + 5 = ?', 15], + [12, 2, 'foo', '12 + 2 = ?', 14], + ]; + } +} diff --git a/www/bug-pwd-finder.php b/www/bug-pwd-finder.php index 08a0feab..023597a3 100644 --- a/www/bug-pwd-finder.php +++ b/www/bug-pwd-finder.php @@ -2,11 +2,14 @@ /* Procedure for emailing a password reminder to a user */ +use App\Utils\Captcha; + +require_once __DIR__.'/../src/Utils/Captcha.php'; + // Start session (for captcha!) session_start(); -require_once 'Text/CAPTCHA/Numeral.php'; -$numeralCaptcha = new Text_CAPTCHA_Numeral(); +$captcha = new Captcha(); // Obtain common includes require_once '../include/prepend.php'; @@ -72,8 +75,7 @@ display_bug_success($success); } -$captcha = $numeralCaptcha->getOperation(); -$_SESSION['answer'] = $numeralCaptcha->getAnswer(); +$_SESSION['answer'] = $captcha->getAnswer(); ?> @@ -90,7 +92,7 @@

      Bug Report ID: # -

      Solve the problem:
      = ?

      +

      Solve the problem:
      getQuestion(); ?>

      diff --git a/www/bug.php b/www/bug.php index 26183b10..46b5854c 100644 --- a/www/bug.php +++ b/www/bug.php @@ -1,6 +1,8 @@ getOperation(); - $_SESSION['answer'] = $numeralCaptcha->getAnswer(); + $_SESSION['answer'] = $captcha->getAnswer(); ?>
      @@ -33,7 +32,7 @@
      Solve the problem:
      = ?
      Solve the problem:
      getQuestion(); ?>
      @@ -1006,7 +1007,7 @@ - + diff --git a/www/gh-pull-add.php b/www/gh-pull-add.php index 616dda54..7dc29654 100644 --- a/www/gh-pull-add.php +++ b/www/gh-pull-add.php @@ -1,5 +1,7 @@ getOperation(); - $_SESSION['answer'] = $numeralCaptcha->getAnswer(); + $_SESSION['answer'] = $captcha->getAnswer(); + if (!empty($_POST['captcha']) && empty($ok_to_submit_report)) { $captcha_label = 'Solve this new problem:'; } else { @@ -541,7 +543,7 @@ } ?> - + From 172d49ac89b234601eb033f5f6453c695b5169f9 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 3 Dec 2018 18:39:39 +0100 Subject: [PATCH 132/277] Bump Composer dependencies Changes: - Add ext-fileinfo to required PHP extensions - composer update --- composer.json | 1 + composer.lock | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index ddb3fa37..d0c20bd6 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ }, "require": { "php": "^7.2", + "ext-fileinfo": "*", "ext-json": "*", "ext-openssl": "*", "ext-pdo": "*", diff --git a/composer.lock b/composer.lock index b0475dab..37ae21cf 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bb74d235e97b99c5049cfbbdbb6a3654", + "content-hash": "59dfac6d80f77829397c4ca16035efb5", "packages": [], "packages-dev": [ { @@ -680,16 +680,16 @@ }, { "name": "phpunit/phpunit", - "version": "7.4.4", + "version": "7.4.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b1be2c8530c4c29c3519a052c9fb6cee55053bbd" + "reference": "61d34e8dd6eb3555900f0f2a2fa9e7e570730102" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b1be2c8530c4c29c3519a052c9fb6cee55053bbd", - "reference": "b1be2c8530c4c29c3519a052c9fb6cee55053bbd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/61d34e8dd6eb3555900f0f2a2fa9e7e570730102", + "reference": "61d34e8dd6eb3555900f0f2a2fa9e7e570730102", "shasum": "" }, "require": { @@ -710,7 +710,7 @@ "phpunit/php-timer": "^2.0", "sebastian/comparator": "^3.0", "sebastian/diff": "^3.0", - "sebastian/environment": "^3.1 || ^4.0", + "sebastian/environment": "^4.0", "sebastian/exporter": "^3.1", "sebastian/global-state": "^2.0", "sebastian/object-enumerator": "^3.0.3", @@ -760,7 +760,7 @@ "testing", "xunit" ], - "time": "2018-11-14T16:52:02+00:00" + "time": "2018-12-03T05:01:24+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1423,6 +1423,7 @@ "prefer-lowest": false, "platform": { "php": "^7.2", + "ext-fileinfo": "*", "ext-json": "*", "ext-openssl": "*", "ext-pdo": "*", From 674023bd052665a400560415f1dbe22006844634 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 4 Dec 2018 00:21:58 +0100 Subject: [PATCH 133/277] Convert UTF-8 with BOM to UTF-8 without BOM --- www/bug.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/bug.php b/www/bug.php index 46b5854c..d0bde4b7 100644 --- a/www/bug.php +++ b/www/bug.php @@ -1,4 +1,4 @@ - Date: Wed, 5 Dec 2018 02:48:41 +0100 Subject: [PATCH 134/277] Remove unused errors handler function handle_pear_errors() Function handle_pear_errors() has been made obsolete via 23298a123688443276f60143c1261f85a85873fe --- include/functions.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/include/functions.php b/include/functions.php index cb6d6aa3..81384044 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1885,21 +1885,6 @@ function get_ticket_links($text) return $matches[1]; } -function handle_pear_errors($error_obj) -{ - error_log($error_obj->getMessage()); - response_header("Oops! We are sorry that you are unable to report an undocumented feature today."); - - $error = "

      Greetings! We are experiencing an error, and in the spirit of Open Source would like you to fix it. "; - $error .= "Or more likely, just wait and someone else will find and solve this.

      \n"; - $error .= "

      It's our guess that the database is down. Argh!!!

      \n"; - - // FIXME: If logged in, show other stuff.... - response_footer($error); - exit; -} - - /** * Generates a random password */ From c8c85a57c5d8a6a0aa752bf3227075df524c243f Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 5 Dec 2018 03:20:01 +0100 Subject: [PATCH 135/277] Replace PEAR error handling with exceptions in pull requests --- include/classes/bug_ghpulltracker.php | 27 ++++++++++++++++++--------- include/functions.php | 5 +---- www/gh-pull-add.php | 18 ++++++++++++------ 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/include/classes/bug_ghpulltracker.php b/include/classes/bug_ghpulltracker.php index 5e7cbee6..6ed05400 100644 --- a/include/classes/bug_ghpulltracker.php +++ b/include/classes/bug_ghpulltracker.php @@ -31,18 +31,27 @@ private function getDataFromGithub($repo, $pull_id) public function attach($bugid, $repo, $pull_id, $developer) { $data = $this->getDataFromGithub($repo, $pull_id); + if (!$data) { - return PEAR::raiseError('Failed to retrieve pull request from GitHub'); - } - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $e = $this->dbh->prepare('INSERT INTO bugdb_pulls - (bugdb_id, github_repo, github_pull_id, github_title, github_html_url, developer) VALUES (?, ?, ?, ?, ?, ?)')->execute( - [$bugid, $repo, $pull_id, $data->title, $data->html_url, $developer]); - PEAR::popErrorHandling(); - if (PEAR::isError($e)) { - return $e; + throw new \Exception('Failed to retrieve pull request from GitHub'); } + $sql = 'INSERT INTO bugdb_pulls + (bugdb_id, github_repo, github_pull_id, github_title, github_html_url, developer) + VALUES (?, ?, ?, ?, ?, ?) + '; + + $arguments = [ + $bugid, + $repo, + $pull_id, + $data->title, + $data->html_url, + $developer, + ]; + + $this->dbh->prepare($sql)->execute($arguments); + return $data; } diff --git a/include/functions.php b/include/functions.php index 81384044..c3a2c6a9 100644 --- a/include/functions.php +++ b/include/functions.php @@ -800,11 +800,8 @@ function show_boolean_options($current) * + string: value is printed * + array: looped through and each value is printed. * If array is empty, nothing is displayed. - * If a value contains a PEAR_Error object, - * + PEAR_Error: prints the value of getMessage() and getUserInfo() - * if DEVBOX is true, otherwise prints data from getMessage(). * - * @param string|array|PEAR_Error $in see long description + * @param string|array $in see long description * @param string $class name of the HTML class for the
      tag. ("errors", "warnings") * @param string $head string to be put above the message * diff --git a/www/gh-pull-add.php b/www/gh-pull-add.php index 7dc29654..f9bf6d6d 100644 --- a/www/gh-pull-add.php +++ b/www/gh-pull-add.php @@ -4,7 +4,6 @@ // Obtain common includes require_once '../include/prepend.php'; -require_once 'PEAR.php'; session_start(); $canpatch = true; @@ -95,11 +94,18 @@ } if (!count($errors)) { - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $newpr = $pullinfo->attach($bug_id, $_POST['repository'], $_POST['pull_id'], $email); - PEAR::popErrorHandling(); - if (PEAR::isError($newpr)) { - $errors = [$newpr->getMessage(), 'Could not attach pull request to Bug #' . $bug_id]; + try { + $newpr = $pullinfo->attach($bug_id, $_POST['repository'], $_POST['pull_id'], $email); + } catch(\Exception $e) { + $errors = ['Could not attach pull request to Bug #'.$bug_id]; + + if ($e->errorInfo[1] === 1062) { + $errors[] = 'This pull request is already added.'; + } + + if (DEVBOX) { + $errors[] = $e->getMessage(); + } } } From 9909448411387a0446cdbb83adc0015bb6225011 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 5 Dec 2018 11:55:34 +0100 Subject: [PATCH 136/277] Remove PEAR errors usage from no-feedback cron job The error handling has been refactored via 23298a123688443276f60143c1261f85a85873fe. --- scripts/cron/no-feedback | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/cron/no-feedback b/scripts/cron/no-feedback index 91ad4ad1..a32b9551 100755 --- a/scripts/cron/no-feedback +++ b/scripts/cron/no-feedback @@ -23,9 +23,7 @@ if ($dbh) FROM bugdb WHERE status = 'Feedback' AND ts2 < DATE_SUB(NOW(), INTERVAL {$after}) ")->execute([]); - if (PEAR::isError($res)) { - throw new Exception("SQL Error in no-feedback"); - } + while ($bug = $res->fetchRow(PDO::FETCH_ASSOC)) { list($mailto, $mailfrom, $bcc, $params) = get_package_mail($bug['package_name'], false, $bug['bug_type']); From a5c3a8b7fc6e94b0c4152e2f06a4969bb2af303f Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 5 Dec 2018 12:28:55 +0100 Subject: [PATCH 137/277] Refactor error handling in quick fixing link This patch refactors error handling from PEAR error to exceptions. Error handling has been refactored via 23298a123688443276f60143c1261f85a85873fe. --- www/fix.php | 56 ++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/www/fix.php b/www/fix.php index 3006bf98..963cb435 100644 --- a/www/fix.php +++ b/www/fix.php @@ -131,43 +131,41 @@ $in['assign'] = $auth_user->handle; } -// Update bug -$dbh->prepare(" - UPDATE bugdb - SET - status = ?, - assign = ?, - ts2 = NOW() - WHERE id = ? -")->execute([ - $status, - $in['assign'], - $bug_id, -]); - -// Add changelog entry -if (!PEAR::isError($res)) { +try { + // Update bug + $dbh->prepare(" + UPDATE bugdb + SET + status = ?, + assign = ?, + ts2 = NOW() + WHERE id = ? + ")->execute([ + $status, + $in['assign'], + $bug_id, + ]); + + // Add changelog entry $changed = bug_diff($bug, $in); if (!empty($changed)) { $log_comment = bug_diff_render_html($changed); if (!empty($log_comment)) { - $res = bugs_add_comment($bug_id, $auth_user->email, $auth_user->name, $log_comment, 'log'); + $result = bugs_add_comment($bug_id, $auth_user->email, $auth_user->name, $log_comment, 'log'); } } -} -// Add possible comment -if (!PEAR::isError($res) && !empty($ncomment)) { - $res = bugs_add_comment($bug_id, $auth_user->email, $auth_user->name, $ncomment, 'comment'); -} + // Add possible comment + if (!empty($ncomment)) { + $result = bugs_add_comment($bug_id, $auth_user->email, $auth_user->name, $ncomment, 'comment'); + } -// Send emails -if (!PEAR::isError($res)) { + // Send emails mail_bug_updates($bug, $in, $auth_user->email, $ncomment); redirect("bug.php?id={$bug_id}&thanks=1"); +} catch (\Exception $e) { + // If we end up here, something went wrong. + response_header('Resolve Bug: Problem'); + display_bug_error($e->getMessage()); + response_footer(); } - -// If we end up here, something went wrong. -response_header('Resolve Bug: Problem'); -display_bug_error($res); -response_footer(); From b4891d7bef11454c4024c8beb577a15cde24e0ae Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 5 Dec 2018 13:38:52 +0100 Subject: [PATCH 138/277] Enable displaying errors for local development environments --- include/prepend.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/prepend.php b/include/prepend.php index 84176079..5a84714d 100644 --- a/include/prepend.php +++ b/include/prepend.php @@ -4,8 +4,6 @@ require_once __DIR__.'/../vendor/autoload.php'; } -ini_set('display_errors', 0); - $site = 'php'; $siteBig = 'PHP'; $ROOT_DIR = realpath(__DIR__ . '/../'); @@ -31,6 +29,13 @@ } // CONFIG END +// Configure errors based on the environment. +if (defined(DEVBOX) && true === DEVBOX) { + ini_set('display_errors', 1); +} else { + ini_set('display_errors', 0); +} + if (!isset($site_data['security_email'])) { $site_data['security_email'] = 'security@php.net'; } From d337e731ff8552e8bd3c3798bbb9a1104343c874 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 5 Dec 2018 15:40:19 +0100 Subject: [PATCH 139/277] Add dual PSR-4 compatible classes autoloader This patch is a workaround until Composer installation step can be used in production bugs.php.net. Once Composer can be added to the deployment step together with rsync this can be simplified and only Composer's autoload.php will be used. --- include/prepend.php | 11 +++ src/Autoloader.php | 201 +++++++++++++++++++++++++++++++++++++++ tests/AutoloaderTest.php | 99 +++++++++++++++++++ www/bug-pwd-finder.php | 6 +- www/bug.php | 1 - www/gh-pull-add.php | 1 - www/patch-add.php | 1 - www/report.php | 1 - 8 files changed, 313 insertions(+), 8 deletions(-) create mode 100644 src/Autoloader.php create mode 100644 tests/AutoloaderTest.php diff --git a/include/prepend.php b/include/prepend.php index 5a84714d..3efa720b 100644 --- a/include/prepend.php +++ b/include/prepend.php @@ -1,7 +1,18 @@ addNamespace('App\\', __DIR__.'/../src/'); } $site = 'php'; diff --git a/src/Autoloader.php b/src/Autoloader.php new file mode 100644 index 00000000..ae526e02 --- /dev/null +++ b/src/Autoloader.php @@ -0,0 +1,201 @@ +addNamespace('Foo\Bar', '/path/to/packages/foo-bar/src'); + * $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/tests'); + * + * The following line would cause the autoloader to attempt to load the + * \Foo\Bar\Qux\Quux class from /path/to/packages/foo-bar/src/Qux/Quux.php: + * + * prefixes[$prefix]) === false) { + $this->prefixes[$prefix] = []; + } + + // retain the base directory for the namespace prefix + if ($prepend) { + array_unshift($this->prefixes[$prefix], $baseDir); + } else { + array_push($this->prefixes[$prefix], $baseDir); + } + } + + /** + * Add a classmap. Classmap is a simplistic imitation of the Composer's + * classmap autoloading. + */ + public function addClassmap($class, $path) + { + $this->classmap[$class] = $path; + } + + /** + * Loads the class file for a given class name. + * + * @param string $class The fully-qualified class name. + * @return mixed The mapped file name on success, or boolean false on + * failure. + */ + public function load($class) + { + // the current namespace prefix + $prefix = $class; + + // Work backwards through the namespace names of the fully-qualified + // class name to find a mapped file name + while (false !== $pos = strrpos($prefix, '\\')) { + + // retain the trailing namespace separator in the prefix + $prefix = substr($class, 0, $pos + 1); + + // the rest is the relative class name + $relativeClass = substr($class, $pos + 1); + + // try to load a mapped file for the prefix and relative class + $mappedFile = $this->loadMappedFile($prefix, $relativeClass); + if ($mappedFile) { + return $mappedFile; + } + + // Remove the trailing namespace separator for the next iteration + // of strrpos() + $prefix = rtrim($prefix, '\\'); + } + + // Check if file is maybe in classmap + if (!empty($this->classmap[$class])) { + return $this->requireFile($this->classmap[$class]) ? $this->classmap[$class] : false; + } + + // Mapped file not found + return false; + } + + /** + * Load the mapped file for a namespace prefix and relative class. + * + * @param string $prefix The namespace prefix. + * @param string $relativeClass The relative class name. + * @return mixed Boolean false if no mapped file can be loaded, or the + * name of the mapped file that was loaded. + */ + protected function loadMappedFile($prefix, $relativeClass) + { + // are there any base directories for this namespace prefix? + if (isset($this->prefixes[$prefix]) === false) { + return false; + } + + // Look through base directories for this namespace prefix + foreach ($this->prefixes[$prefix] as $baseDir) { + // replace the namespace prefix with the base directory, + // replace namespace separators with directory separators + // in the relative class name, append with .php + $file = $baseDir + . str_replace('\\', '/', $relativeClass) + . '.php'; + + // If the mapped file exists, require it + if ($this->requireFile($file)) { + return $file; + } + } + + // Mapped file not found + return false; + } + + /** + * If a file exists, require it from the file system. + * + * @param string $file The file to require. + * @return bool True if the file exists, false if not. + */ + protected function requireFile($file) + { + if (file_exists($file)) { + require_once $file; + return true; + } + + return false; + } +} diff --git a/tests/AutoloaderTest.php b/tests/AutoloaderTest.php new file mode 100644 index 00000000..16ef8160 --- /dev/null +++ b/tests/AutoloaderTest.php @@ -0,0 +1,99 @@ +files = $files; + } + + protected function requireFile($file) + { + return in_array($file, $this->files); + } +} + +class AutoloaderTest extends TestCase +{ + protected $autoloader; + + protected function setUp() + { + $this->autoloader = new MockAutoloader; + + $this->autoloader->setFiles([ + '/vendor/foo.bar/src/ClassName.php', + '/vendor/foo.bar/src/DoomClassName.php', + '/vendor/foo.bar/tests/ClassNameTest.php', + '/vendor/foo.bardoom/src/ClassName.php', + '/vendor/foo.bar.baz.dib/src/ClassName.php', + '/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php', + '/src/lib/ClassName.php', + '/src/libfoo/ClassFoo.php', + ]); + + $this->autoloader->addNamespace( + 'Foo\Bar', + '/vendor/foo.bar/src' + ); + + $this->autoloader->addNamespace( + 'Foo\Bar', + '/vendor/foo.bar/tests' + ); + + $this->autoloader->addNamespace( + 'Foo\\BarDoom', + '/vendor/foo.bardoom/src/' + ); + + $this->autoloader->addNamespace( + 'Foo\Bar\Baz\Dib', + '/vendor/foo.bar.baz.dib/src/' + ); + + $this->autoloader->addNamespace( + 'Foo\Bar\Baz\Dib\Zim\Gir', + '/vendor/foo.bar.baz.dib.zim.gir/src/' + ); + + $this->autoloader->addClassmap( + 'ClassName', + '/src/lib/ClassName.php' + ); + + $this->autoloader->addClassmap( + 'ClassFoo', + '/src/libfoo/ClassFoo.php' + ); + } + + /** + * @dataProvider classesProvider + */ + public function testLoad($class, $expected) + { + $this->assertEquals($expected, $this->autoloader->load($class)); + } + + public function classesProvider() + { + return [ + ['Foo\Bar\ClassName', '/vendor/foo.bar/src/ClassName.php'], + ['Foo\Bar\ClassNameTest', '/vendor/foo.bar/tests/ClassNameTest.php'], + ['ClassName', '/src/lib/ClassName.php'], + ['ClassFoo', '/src/libfoo/ClassFoo.php'], + ['No_Vendor\No_Package\NoClass', false], + ['Foo\Bar\Baz\Dib\Zim\Gir\ClassName', '/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php'], + ['Foo\Bar\DoomClassName', '/vendor/foo.bar/src/DoomClassName.php'], + ['Foo\BarDoom\ClassName', '/vendor/foo.bardoom/src/ClassName.php'], + ]; + } +} diff --git a/www/bug-pwd-finder.php b/www/bug-pwd-finder.php index 023597a3..ffe8a38c 100644 --- a/www/bug-pwd-finder.php +++ b/www/bug-pwd-finder.php @@ -4,16 +4,14 @@ use App\Utils\Captcha; -require_once __DIR__.'/../src/Utils/Captcha.php'; +// Obtain common includes +require_once '../include/prepend.php'; // Start session (for captcha!) session_start(); $captcha = new Captcha(); -// Obtain common includes -require_once '../include/prepend.php'; - $errors = []; $success = false; $bug_id = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0; diff --git a/www/bug.php b/www/bug.php index d0bde4b7..e2c65767 100644 --- a/www/bug.php +++ b/www/bug.php @@ -115,7 +115,6 @@ // captcha is not necessary if the user is logged in if (!$logged_in) { - require_once __DIR__.'/../src/Utils/Captcha.php'; $captcha = new Captcha(); } diff --git a/www/gh-pull-add.php b/www/gh-pull-add.php index f9bf6d6d..93e147a6 100644 --- a/www/gh-pull-add.php +++ b/www/gh-pull-add.php @@ -38,7 +38,6 @@ // captcha is not necessary if the user is logged in if (!$logged_in) { - require_once __DIR__.'/../src/Utils/Captcha.php'; $captcha = new Captcha(); } diff --git a/www/patch-add.php b/www/patch-add.php index 6fd70352..0b1db805 100644 --- a/www/patch-add.php +++ b/www/patch-add.php @@ -38,7 +38,6 @@ // captcha is not necessary if the user is logged in if (!$logged_in) { - require_once __DIR__.'/../src/Utils/Captcha.php'; $captcha = new Captcha(); } diff --git a/www/report.php b/www/report.php index 542bf877..c2a52f4a 100644 --- a/www/report.php +++ b/www/report.php @@ -25,7 +25,6 @@ // captcha is not necessary if the user is logged in if (!$logged_in) { - require_once __DIR__.'/../src/Utils/Captcha.php'; $captcha = new Captcha(); } From 3f343755266d5fd669b115f86ea279d4c49567f8 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 5 Dec 2018 17:50:05 +0100 Subject: [PATCH 140/277] Fix typo --- include/prepend.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/prepend.php b/include/prepend.php index 3efa720b..60fdd8c3 100644 --- a/include/prepend.php +++ b/include/prepend.php @@ -41,7 +41,7 @@ // CONFIG END // Configure errors based on the environment. -if (defined(DEVBOX) && true === DEVBOX) { +if (defined('DEVBOX') && true === DEVBOX) { ini_set('display_errors', 1); } else { ini_set('display_errors', 0); From 73062503a65bfa62d5f2f365ee99269225580bb9 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 5 Dec 2018 17:55:06 +0100 Subject: [PATCH 141/277] Refactor database classes --- include/classes/bug_pdo.php | 73 ------------------------------------- include/prepend.php | 10 ++--- src/Database/Database.php | 38 +++++++++++++++++++ src/Database/Statement.php | 46 +++++++++++++++++++++++ 4 files changed, 89 insertions(+), 78 deletions(-) delete mode 100644 include/classes/bug_pdo.php create mode 100644 src/Database/Database.php create mode 100644 src/Database/Statement.php diff --git a/include/classes/bug_pdo.php b/include/classes/bug_pdo.php deleted file mode 100644 index 536762ce..00000000 --- a/include/classes/bug_pdo.php +++ /dev/null @@ -1,73 +0,0 @@ - - */ - -class Bug_PDO extends PDO -{ - /** - * When creating new PDO object, automagically switch PDOStatement with - * own extended implementation. - */ - public function __construct($dsn, $username = '', $password = '', array $options = []) - { - parent::__construct($dsn, $username, $password, $options); - - $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['Bug_PDOStatement']); - } - - /** - * PDO puts apostrophes around the text so we need to strip the outermost - * characters. - */ - public function escape($text, $escape_wildcards = false) - { - return substr($this->quote($text), 1, -1); - } - - public function queryAll($query, $types = null, $fetchmode = null, $rekey = false, $force_array = false, $group = false) - { - return $this->query($query)->fetchAll(); - } -} - -class Bug_PDOStatement extends PDOStatement -{ - /** - * This allows chaining execute() method: - * $db->query('SELECT a FROM b WHERE c = ?')->execute('foo')->fetch(); - * PDOStatement::execute(), on the other hand, returns boolean. Change it to - * return $this and thus allow further method chaining. - */ - public function execute($input_parameters = NULL) - { - parent::execute($input_parameters); - - return $this; - } - - public function fetchAll($fetchode = null, $rekey = false, $force_array = false, $group = false) - { - return parent::fetchAll(); - } - - public function fetchCol($colnum) - { - return parent::fetchColumn($colnum); - } - - public function fetchOne($colnum = 0, $rownum = null) - { - return $this->fetch(PDO::FETCH_NUM)[0]; - } - - public function fetchRow($mode=NULL) - { - if (!$mode) { - $mode = PDO::FETCH_BOTH; - } - return $this->fetch($mode); - } -} diff --git a/include/prepend.php b/include/prepend.php index 60fdd8c3..9e03d698 100644 --- a/include/prepend.php +++ b/include/prepend.php @@ -1,6 +1,7 @@ PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_EMULATE_PREPARES => false, +$dbh = new Database(DATABASE_DSN, $site_data['db_user'], $site_data['db_pass'], [ + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, + \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, + \PDO::ATTR_EMULATE_PREPARES => false, ]); // Last Updated.. diff --git a/src/Database/Database.php b/src/Database/Database.php new file mode 100644 index 00000000..37ae753b --- /dev/null +++ b/src/Database/Database.php @@ -0,0 +1,38 @@ + + */ +class Database extends \PDO +{ + /** + * When creating new PDO object, automagically switch PDOStatement with own + * extended implementation. + */ + public function __construct(string $dsn, string $username = '', string $password = '', array $options = []) + { + parent::__construct($dsn, $username, $password, $options); + + $this->setAttribute(\PDO::ATTR_STATEMENT_CLASS, [Statement::class]); + } + + /** + * PDO puts apostrophes around the text so we need to strip the outermost + * characters. + */ + public function escape($text, $escape_wildcards = false) + { + return substr($this->quote($text), 1, -1); + } + + public function queryAll($query, $types = null, $fetchmode = null, $rekey = false, $force_array = false, $group = false) + { + return $this->query($query)->fetchAll(); + } +} diff --git a/src/Database/Statement.php b/src/Database/Statement.php new file mode 100644 index 00000000..75b545b3 --- /dev/null +++ b/src/Database/Statement.php @@ -0,0 +1,46 @@ +query('SELECT a FROM b WHERE c = ?')->execute('foo')->fetch(); + * \PDOStatement::execute(), on the other hand, returns boolean. Change it + * to return $this and thus allow further method chaining. + */ + public function execute($input_parameters = null) + { + parent::execute($input_parameters); + + return $this; + } + + public function fetchAll($fetchode = null, $rekey = false, $force_array = false, $group = false) + { + return parent::fetchAll(); + } + + public function fetchCol($colnum) + { + return parent::fetchColumn($colnum); + } + + public function fetchOne($colnum = 0, $rownum = null) + { + return $this->fetch(\PDO::FETCH_NUM)[0]; + } + + public function fetchRow($mode = null) + { + if (!$mode) { + $mode = \PDO::FETCH_BOTH; + } + + return $this->fetch($mode); + } +} From 44706d2e93c7d334710b9f1b202005fea6f9a770 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 5 Dec 2018 17:19:08 +0100 Subject: [PATCH 142/277] Refactor pull requests service class Changes: - Class moved to src under App namespace - Code refactored into repository class and GitHub client for managing pull requests - When new pull request is attached to a bug report the info comment is also added to bug activity as is practice with other activities already. --- include/classes/bug_ghpulltracker.php | 85 ------------------- src/Repository/PullRequestRepository.php | 39 +++++++++ src/Utils/GitHub.php | 101 +++++++++++++++++++++++ www/bug.php | 6 +- www/gh-pull-add.php | 20 +++-- 5 files changed, 154 insertions(+), 97 deletions(-) delete mode 100644 include/classes/bug_ghpulltracker.php create mode 100644 src/Repository/PullRequestRepository.php create mode 100644 src/Utils/GitHub.php diff --git a/include/classes/bug_ghpulltracker.php b/include/classes/bug_ghpulltracker.php deleted file mode 100644 index 6ed05400..00000000 --- a/include/classes/bug_ghpulltracker.php +++ /dev/null @@ -1,85 +0,0 @@ -dbh = $GLOBALS['dbh']; - } - - private function getDataFromGithub($repo, $pull_id) - { - $ctxt = stream_context_create([ - 'http' => [ - 'ignore_errors' => '1', - 'user_agent' => $this->userAgent, - ] - ]); - $data = @json_decode(file_get_contents("https://api.github.com/repos/php/".urlencode($repo).'/pulls/'.((int)$pull_id), null, $ctxt)); - if (!is_object($data)) { - return false; - } - return $data; - } - - /** - * Attach a pull request to this bug - */ - public function attach($bugid, $repo, $pull_id, $developer) - { - $data = $this->getDataFromGithub($repo, $pull_id); - - if (!$data) { - throw new \Exception('Failed to retrieve pull request from GitHub'); - } - - $sql = 'INSERT INTO bugdb_pulls - (bugdb_id, github_repo, github_pull_id, github_title, github_html_url, developer) - VALUES (?, ?, ?, ?, ?, ?) - '; - - $arguments = [ - $bugid, - $repo, - $pull_id, - $data->title, - $data->html_url, - $developer, - ]; - - $this->dbh->prepare($sql)->execute($arguments); - - return $data; - } - - /** - * Remove a pull request from this bug - */ - public function detach($bugid, $repo, $pull_id) - { - $this->dbh->prepare('DELETE FROM bugdb_pulls - WHERE bugdb_id = ? and github_repo = ? and github_pull_id = ?')->execute( - [$bugid, $repo, $pull_id]); - } - - /** - * Retrieve a listing of all pull requests - * - * @param int $bugid - * @return array - */ - public function listPulls($bugid) - { - $query = ' - SELECT github_repo, github_pull_id, github_title, github_html_url, developer - FROM bugdb_pulls - WHERE bugdb_id = ? - ORDER BY github_repo, github_pull_id DESC - '; - - return $this->dbh->prepare($query)->execute([$bugid])->fetchAll(PDO::FETCH_ASSOC); - } -} diff --git a/src/Repository/PullRequestRepository.php b/src/Repository/PullRequestRepository.php new file mode 100644 index 00000000..64ab66a8 --- /dev/null +++ b/src/Repository/PullRequestRepository.php @@ -0,0 +1,39 @@ +dbh = $dbh; + } + + /** + * Retrieve all pull requests by bug id. + * + * @param int $bugId + * @return array + */ + public function findAllByBugId(int $bugId) + { + $sql = 'SELECT github_repo, github_pull_id, github_title, github_html_url, developer + FROM bugdb_pulls + WHERE bugdb_id = ? + ORDER BY github_repo, github_pull_id DESC + '; + + return $this->dbh->prepare($sql)->execute([$bugId])->fetchAll(); + } +} diff --git a/src/Utils/GitHub.php b/src/Utils/GitHub.php new file mode 100644 index 00000000..c551959c --- /dev/null +++ b/src/Utils/GitHub.php @@ -0,0 +1,101 @@ +dbh = $dbh; + } + + /** + * Retrieve data from remote GitHub URL. + */ + private function getDataFromGithub(string $repo, int $pullId) + { + $context = stream_context_create([ + 'http' => [ + 'ignore_errors' => '1', + 'user_agent' => $this->userAgent, + ] + ]); + + $url = $this->url.'/repos/'.$this->organization.'/'.urlencode($repo).'/pulls/'.$pullId; + $data = @json_decode(file_get_contents($url, null, $context)); + + if (!is_object($data)) { + return false; + } + + return $data; + } + + /** + * Attach a pull request to bug. + */ + public function attach($bugId, $repo, $pullId, $developer) + { + $data = $this->getDataFromGithub($repo, (int)$pullId); + + if (!$data) { + throw new \Exception('Failed to retrieve pull request from GitHub'); + } + + $sql = 'INSERT INTO bugdb_pulls + (bugdb_id, github_repo, github_pull_id, github_title, github_html_url, developer) + VALUES (?, ?, ?, ?, ?, ?) + '; + + $arguments = [ + $bugId, + $repo, + $pullId, + $data->title, + $data->html_url, + $developer, + ]; + + $this->dbh->prepare($sql)->execute($arguments); + + return $data; + } + + /** + * Remove a pull request from given bug. + */ + public function detach(int $bugId, string $repo, int $pullId) + { + $sql = 'DELETE FROM bugdb_pulls + WHERE bugdb_id = ? AND github_repo = ? AND github_pull_id = ? + '; + + $this->dbh->prepare($sql)->execute([$bugId, $repo, $pullId]); + } +} diff --git a/www/bug.php b/www/bug.php index e2c65767..c676d139 100644 --- a/www/bug.php +++ b/www/bug.php @@ -2,6 +2,7 @@ /* User interface for viewing and editing bug details */ use App\Utils\Captcha; +use App\Repository\PullRequestRepository; // Obtain common includes require_once '../include/prepend.php'; @@ -1085,9 +1086,8 @@ } echo "

      Add a Patch

      "; - require_once "{$ROOT_DIR}/include/classes/bug_ghpulltracker.php"; - $pulltracker = new Bug_Pulltracker(); - $pulls = $pulltracker->listPulls($bug_id); + $pullRequestRepository = new PullRequestRepository($dbh); + $pulls = $pullRequestRepository->findAllByBugId($bug_id); echo "

      Pull Requests

      \n"; require "{$ROOT_DIR}/templates/listpulls.php"; diff --git a/www/gh-pull-add.php b/www/gh-pull-add.php index 93e147a6..c86d4bd4 100644 --- a/www/gh-pull-add.php +++ b/www/gh-pull-add.php @@ -1,6 +1,8 @@ listPulls($bug_id); + $pulls = $pullRequestRepository->findAllByBugId($bug_id); include "{$ROOT_DIR}/templates/addghpull.php"; exit; } @@ -109,12 +111,11 @@ } if (count($errors)) { - $pulls = $pullinfo->listPulls($bug_id); + $pulls = $pullRequestRepository->findAllByBugId($bug_id); include "{$ROOT_DIR}/templates/addghpull.php"; exit; } -/* // Add a comment to the bug report. $text = <<email, $auth_user->name, $text, 'patch'); // Send emails - mail_bug_updates($buginfo, $buginfo, $auth_user->email, $text, 4, $bug_id); - */ - $pulls = $pullinfo->listPulls($bug_id); + // TODO: enable also mailing + //mail_bug_updates($buginfo, $buginfo, $auth_user->email, $text, 4, $bug_id); + + $pulls = $pullRequestRepository->findAllByBugId($bug_id); $errors = []; include "{$ROOT_DIR}/templates/addghpull.php"; exit; } $email = isset($_GET['email']) ? $_GET['email'] : ''; -$pulls = $pullinfo->listPulls($bug_id); +$pulls = $pullRequestRepository->findAllByBugId($bug_id); include "{$ROOT_DIR}/templates/addghpull.php"; From 487a0bc254f1bf1cc98df09fb33f7a6068f6620a Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 5 Dec 2018 19:24:10 +0100 Subject: [PATCH 143/277] Add mbstring extension to required extensions --- composer.json | 1 + composer.lock | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d0c20bd6..39c9f3de 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "php": "^7.2", "ext-fileinfo": "*", "ext-json": "*", + "ext-mbstring": "*", "ext-openssl": "*", "ext-pdo": "*", "ext-pdo_mysql": "*", diff --git a/composer.lock b/composer.lock index 37ae21cf..7d273408 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "59dfac6d80f77829397c4ca16035efb5", + "content-hash": "dc3b11918cde7a2af15820af62cb92af", "packages": [], "packages-dev": [ { @@ -1425,6 +1425,7 @@ "php": "^7.2", "ext-fileinfo": "*", "ext-json": "*", + "ext-mbstring": "*", "ext-openssl": "*", "ext-pdo": "*", "ext-pdo_mysql": "*", From 8ab9a1d1103117a6a185262ebeadba6c2595dcd4 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 5 Dec 2018 14:51:46 +0100 Subject: [PATCH 144/277] Refactor patches uploading This patch moves patches uploading functionality from the outdated HTTP_Upload package to a dedicated service class in the app. Additional changes in this context: - Functionality concerning retrieving patches data from database has been moved to a separate repository classes. - Some missed bugs fixed when uploading patches and no developer info were recorded. - Obsoleting patches functionality is now working again. - Added a simple unit test. --- README.md | 2 +- include/classes/bug_patchtracker.php | 364 --------------------- include/functions.php | 2 +- src/Repository/ObsoletePatchRepository.php | 51 +++ src/Repository/PatchRepository.php | 106 ++++++ src/Utils/PatchTracker.php | 242 ++++++++++++++ src/Utils/Uploader.php | 219 +++++++++++++ templates/addpatch.php | 16 +- tests/Utils/UploaderTest.php | 54 +++ tests/fixtures/files/patch.txt | 1 + www/bug.php | 13 +- www/patch-add.php | 59 ++-- www/patch-display.php | 36 +- www/report.php | 15 +- 14 files changed, 752 insertions(+), 428 deletions(-) delete mode 100644 include/classes/bug_patchtracker.php create mode 100644 src/Repository/ObsoletePatchRepository.php create mode 100644 src/Repository/PatchRepository.php create mode 100644 src/Utils/PatchTracker.php create mode 100644 src/Utils/Uploader.php create mode 100644 tests/Utils/UploaderTest.php create mode 100644 tests/fixtures/files/patch.txt diff --git a/README.md b/README.md index 564aa476..7e353d0d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ composer install ```bash pear channel-update pear.php.net -pear install --alldeps Text_Diff HTTP_Upload-1.0.0b4 +pear install --alldeps Text_Diff ``` * Database: diff --git a/include/classes/bug_patchtracker.php b/include/classes/bug_patchtracker.php deleted file mode 100644 index 18c01d29..00000000 --- a/include/classes/bug_patchtracker.php +++ /dev/null @@ -1,364 +0,0 @@ -uploader = false; - $this->dbh = $GLOBALS['dbh']; - return; - } - } - $this->uploader = new HTTP_Upload('en'); - $this->dbh = $GLOBALS['dbh']; - } - - /** - * Return the directory in which patches should be stored - * - * @param int $bugid - * @param string $name name of this patch line - * @return string - */ - private function patchDir($bugid, $name) - { - return BUG_PATCHTRACKER_TMPDIR . '/p' . $bugid . '/' . $name; - } - /** - * Create the directory in which patches for this bug ID will be stored - * - * @param int $bugid - */ - private function setupPatchDir($bugid, $name) - { - if (file_exists($this->patchDir($bugid, $name))) { - if (!is_dir($this->patchDir($bugid, $name))) { - return PEAR::raiseError('Cannot create patch storage for Bug #' . $bugid . - ', storage directory exists and is not a directory'); - } - return; - } - if (!file_exists(dirname($this->patchDir($bugid, $name)))) { - // setup bug directory - if (!@mkdir(dirname($this->patchDir($bugid, $name)))) { - require_once 'PEAR.php'; - return PEAR::raiseError('Cannot create patch storage for Bug #' . $bugid); - } - } elseif (!is_dir(dirname($this->patchDir($bugid, $name)))) { - return PEAR::raiseError('Cannot create patch storage for Bug #' . $bugid . - ', storage directory exists and is not a directory'); - } - // setup patch directory - if (!@mkdir($this->patchDir($bugid, $name))) { - require_once 'PEAR.php'; - return PEAR::raiseError('Cannot create patch storage for Bug #' . $bugid); - } - } - - /** - * Retrieve a unique, ordered patch filename - * - * @param int $bugid - * @param string $patch - * @return array array(revision, patch file name) - */ - private function newPatchFileName($bugid, $patch, $handle) - { - $id = time(); - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $e = $this->dbh->prepare('INSERT INTO bugdb_patchtracker - (bugdb_id, patch, revision, developer) VALUES(?, ?, ?, ?)')->execute( - [$bugid, $patch, $id, $handle]); - if (PEAR::isError($e)) { - // try with another timestamp - $id++; - $e = $this->dbh->prepare('INSERT INTO bugdb_patchtracker - (bugdb_id, patch, revision, developer) VALUES(?, ?, ?, ?)')->execute( - [$bugid, $patch, $id, $handle]); - } - PEAR::popErrorHandling(); - if (PEAR::isError($e)) { - return PEAR::raiseError("Could not get unique patch file name for bug #{$bugid}, patch \"{$patch}\""); - } - return [$id, $this->getPatchFileName($id)]; - } - - /** - * Retrieve the name of the patch file on the system - * - * @param int $id revision ID - * @return string - */ - private function getPatchFileName($id) - { - return 'p' . $id . '.patch.txt'; - } - - /** - * Retrieve the full path to a patch file - * - * @param int $bugid - * @param string $name - * @param int $revision - * @return string - */ - public function getPatchFullpath($bugid, $name, $revision) - { - return $this->patchDir($bugid, $name).'/'.$this->getPatchFileName($revision); - } - - /** - * Attach a patch to this bug - * - * @param int $bugid - * @param string $patch uploaded patch filename form variable - * @param string $name patch name (allows several patches to be versioned) - * @param string $handle developer handle - * @param array $obsoletes obsoleted patches - * @return int patch revision - */ - public function attach($bugid, $patch, $name, $handle, $obsoletes) - { - if (!$this->uploader) { - return PEAR::raiseError('Upload directory for patches could not be initialized'); - } - if (!preg_match('/^[\w\-\.]+\z/', $name) || strlen($name) > 80) { - return PEAR::raiseError("Invalid patch name \"".htmlspecialchars($name)."\""); - } - if (!is_array($obsoletes)) { - return PEAR::raiseError('Invalid obsoleted patches'); - } - - $file = $this->uploader->getFiles($patch); - if (PEAR::isError($file)) { - return $file; - } - - if ($file->isValid()) { - $newobsoletes = []; - foreach ($obsoletes as $who) { - if (!$who) { - continue; // remove (none) - } - $who = explode('#', $who); - if (count($who) != 2) { - continue; - } - if (file_exists($this->getPatchFullpath($bugid, $who[0], $who[1]))) { - $newobsoletes[] = $who; - } - } - if (PEAR::isError($e = $this->setupPatchDir($bugid, $name))) { - return $e; - } - - $res = $this->newPatchFileName($bugid, $name, $handle); - if (PEAR::isError($res)) { - return $res; - } - list($id, $fname) = $res; - $file->setName($fname); - $allowed_mime_types = [ - 'application/x-txt', - 'text/plain', - 'text/x-diff', - 'text/x-patch', - 'text/x-c++', - 'text/x-c', - 'text/x-m4', - ]; - - // return mime type ala mimetype extension - if (class_exists('finfo')) { - $finfo = new finfo(FILEINFO_MIME); - if (!$finfo) { - return PEAR::raiseError('Error: Opening fileinfo database failed'); - } - - // get mime-type for a specific file - $mime = $finfo->file($file->getProp('tmp_name')); - // get rid of the charset part - $t = explode(';', $mime); - $mime = $t[0]; - } - else // NOTE: I didn't have PHP 5.3 around with fileinfo enabled :) - { - $mime = 'text/plain'; - } - if (!in_array($mime, $allowed_mime_types)) { - $this->dbh->prepare('DELETE FROM bugdb_patchtracker - WHERE bugdb_id = ? and patch = ? and revision = ?')->execute( - [$bugid, $name, $id]); - return PEAR::raiseError('Error: uploaded patch file must be text' - . ' file (save as e.g. "patch.txt" or "package.diff")' - . ' (detected "' . htmlspecialchars($mime) . '")' - ); - } - $tmpfile = $file->moveTo($this->patchDir($bugid, $name)); - if (PEAR::isError($tmpfile)) { - $this->dbh->prepare('DELETE FROM bugdb_patchtracker - WHERE bugdb_id = ? and patch = ? and revision = ?')->execute( - [$bugid, $name, $id]); - return $tmpfile; - } - if (!$file->getProp('size')) { - $this->detach($bugid, $name, $id); - return PEAR::raiseError('zero-length patches not allowed'); - } - if ($file->getProp('size') > 102400) { - $this->detach($bugid, $name, $id); - return PEAR::raiseError('Patch files cannot be larger than 100k'); - } - foreach ($newobsoletes as $obsolete) { - $this->obsoletePatch($bugid, $name, $id, $obsolete[0], $obsolete[1]); - } - return $id; - } elseif ($file->isMissing()) { - return PEAR::raiseError('Uploaded file is empty or nothing was uploaded.'); - } elseif ($file->error()) { - return PEAR::raiseError($file->errorMsg()); - } - return PEAR::raiseError('Unable to attach patch (try renaming the file with .txt extension)'); - } - - /** - * Remove a patch revision from this bug - * - * @param int $bugid - * @param string $name - * @param int $revision - */ - public function detach($bugid, $name, $revision) - { - $this->dbh->prepare('DELETE FROM bugdb_patchtracker - WHERE bugdb_id = ? and patch = ? and revision = ?')->execute( - [$bugid, $name, $revision]); - - @unlink($this->patchDir($bugid, $name).'/'.$this->getPatchFileName($revision)); - } - - /** - * Retrieve the actual contents of the patch - * - * @param int $bugid - * @param string $name - * @param int $revision - * @return string - */ - public function getPatch($bugid, $name, $revision) - { - if ($this->dbh->prepare(' - SELECT bugdb_id - FROM bugdb_patchtracker - WHERE bugdb_id = ? AND patch = ? AND revision = ?')->execute([$bugid, $name, $revision])->fetchOne() - ) { - $contents = @file_get_contents($this->getPatchFullpath($bugid, $name, $revision)); - if (!$contents) { - return PEAR::raiseError('Cannot retrieve patch revision "' . $revision . '" for patch "' . $name . '"'); - } - return $contents; - } - return PEAR::raiseError('No such patch revision "' . $revision . '", or no such patch "' . $name . '"'); - } - - /** - * Retrieve a listing of all patches and their revisions - * - * @param int $bugid - * @return array - */ - public function listPatches($bugid) - { - $query = ' - SELECT patch, revision, developer - FROM bugdb_patchtracker - WHERE bugdb_id = ? - ORDER BY revision DESC - '; - - return $this->dbh->prepare($query)->execute([$bugid])->fetchAll(PDO::FETCH_NUM, true, false, true); - } - - /** - * Retrieve a listing of all patches and their revisions - * - * @param int $bugid - * @param string $patch - * @return array - */ - public function listRevisions($bugid, $patch) - { - $query = ' - SELECT revision FROM bugdb_patchtracker - WHERE bugdb_id = ? AND patch = ? - ORDER BY revision DESC - '; - return $this->dbh->prepare($query)->execute([$bugid, $patch])->fetchAll(PDO::FETCH_NUM); - } - - /** - * Retrieve the developer who uploaded this patch - * - * @param int $bugid - * @param string $patch - * @param int $revision - * @return string|array array if no revision is selected - */ - public function getDeveloper($bugid, $patch, $revision = false) - { - if ($revision) { - return $this->dbh->prepare(' - SELECT developer - FROM bugdb_patchtracker - WHERE bugdb_id = ? AND patch = ? AND revision = ? - ')->execute([$bugid, $patch, $revision])->fetchOne(); - } - return $this->dbh->prepare(' - SELECT developer, revision - FROM bugdb_patchtracker - WHERE bugdb_id = ? AND patch = ? ORDER BY revision DESC - ')->execute([$bugid, $patch])->fetchAll(PDO::FETCH_ASSOC); - } - - public function getObsoletingPatches($bugid, $patch, $revision) - { - return $this->dbh->prepare(' - SELECT bugdb_id, patch, revision - FROM bugdb_obsoletes_patches - WHERE bugdb_id = ? AND obsolete_patch = ? AND obsolete_revision = ? - ')->execute([$bugid, $patch, $revision])->fetchAll(PDO::FETCH_ASSOC); - } - - public function getObsoletePatches($bugid, $patch, $revision) - { - return $this->dbh->prepare(' - SELECT bugdb_id, obsolete_patch, obsolete_revision - FROM bugdb_obsoletes_patches - WHERE bugdb_id = ? AND patch = ? AND revision = ? - ')->execute([$bugid, $patch, $revision])->fetchAll(PDO::FETCH_ASSOC); - } - - /** - * link to an obsolete patch from the new one - * - * @param int $bugid - * @param string $name better patch name - * @param int $revision better patch revision - * @param string $obsoletename - * @param int $obsoleterevision - */ - private function obsoletePatch($bugid, $name, $revision, $obsoletename, $obsoleterevision) - { - $this->dbh->prepare(' - INSERT INTO bugdb_obsoletes_patches - VALUES(?, ?, ?, ?, ?) - ')->execute([$bugid, $name, $revision, $obsoletename, $obsoleterevision]); - } -} diff --git a/include/functions.php b/include/functions.php index c3a2c6a9..8f35fe22 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1148,7 +1148,7 @@ function format_date($ts = null, $format = 'Y-m-d H:i e') if (!$ts) { $ts = time(); } - return gmdate($format, $ts - date('Z', $ts)); + return gmdate($format, (int)$ts - date('Z', (int)$ts)); } /** diff --git a/src/Repository/ObsoletePatchRepository.php b/src/Repository/ObsoletePatchRepository.php new file mode 100644 index 00000000..2335ec9e --- /dev/null +++ b/src/Repository/ObsoletePatchRepository.php @@ -0,0 +1,51 @@ +dbh = $dbh; + } + + /** + * Retrieve patches that obsoleted given patch. + */ + public function findObsoletingPatches(int $bugId, string $patch, int $revision): array + { + $sql = 'SELECT bugdb_id, patch, revision + FROM bugdb_obsoletes_patches + WHERE bugdb_id = ? AND obsolete_patch = ? AND obsolete_revision = ? + '; + + return $this->dbh->prepare($sql)->execute([$bugId, $patch, $revision])->fetchAll(); + } + + /** + * Retrieve obsolete patches by bug, patch and revision. + */ + public function findObsoletePatches(int $bugId, string $patch, int $revision): array + { + $sql = 'SELECT bugdb_id, obsolete_patch, obsolete_revision + FROM bugdb_obsoletes_patches + WHERE bugdb_id = ? AND patch = ? AND revision = ? + '; + + return $this->dbh->prepare($sql)->execute([$bugId, $patch, $revision])->fetchAll(); + } +} diff --git a/src/Repository/PatchRepository.php b/src/Repository/PatchRepository.php new file mode 100644 index 00000000..a3a6d87e --- /dev/null +++ b/src/Repository/PatchRepository.php @@ -0,0 +1,106 @@ +dbh = $dbh; + $this->uploadsDir = BUG_PATCHTRACKER_TMPDIR; + } + + /** + * Retrieve a list of all patches and their revisions by given bug id. + */ + public function findAllByBugId(int $bugId): array + { + $sql = 'SELECT patch, revision, developer + FROM bugdb_patchtracker + WHERE bugdb_id = ? + ORDER BY revision DESC + '; + + return $this->dbh->prepare($sql)->execute([$bugId])->fetchAll(); + } + + /** + * Retrieve the developer by patch. + */ + public function findDeveloper(int $bugId, string $patch, int $revision): string + { + $sql = 'SELECT developer + FROM bugdb_patchtracker + WHERE bugdb_id = ? AND patch = ? AND revision = ? + '; + + $arguments = [$bugId, $patch, $revision]; + + return $this->dbh->prepare($sql)->execute($arguments)->fetchOne(); + } + + /** + * Retrieve a list of all patches and their revisions. + */ + public function findRevisions(int $bugId, string $patch): array + { + $sql = 'SELECT revision + FROM bugdb_patchtracker + WHERE bugdb_id = ? AND patch = ? + ORDER BY revision DESC + '; + + return $this->dbh->prepare($sql)->execute([$bugId, $patch])->fetchAll(); + } + + /** + * Retrieve the actual contents of the patch. + */ + public function getPatchContents(int $bugId, string $name, int $revision): string + { + $sql = 'SELECT bugdb_id + FROM bugdb_patchtracker + WHERE bugdb_id = ? AND patch = ? AND revision = ? + '; + + if ($this->dbh->prepare($sql)->execute([$bugId, $name, $revision])->fetchOne()) { + $contents = @file_get_contents($this->getPatchPath($bugId, $name, $revision)); + + if (!$contents) { + throw new \Exception('Cannot retrieve patch revision "'.$revision.'" for patch "'.$name.'"'); + } + + return $contents; + } + + throw new \Exception('No such patch revision "'.$revision.'", or no such patch "'.$name.'"'); + } + + /** + * Get absolute patch file name. + */ + private function getPatchPath(int $bugId, string $name, int $revision): string + { + return $this->uploadsDir.'/p'.$bugId.'/'.$name.'/'.'p'.$revision.'.patch.txt'; + } +} diff --git a/src/Utils/PatchTracker.php b/src/Utils/PatchTracker.php new file mode 100644 index 00000000..127831a1 --- /dev/null +++ b/src/Utils/PatchTracker.php @@ -0,0 +1,242 @@ +dbh = $dbh; + $this->uploadsDir = BUG_PATCHTRACKER_TMPDIR; + + $this->uploader = $uploader; + $this->uploader->setMaxFileSize(self::MAX_FILE_SIZE); + $this->uploader->setValidMediaTypes(self::VALID_MEDIA_TYPES); + } + + /** + * Create a parent uploads directory for patches if it is missing. + */ + private function createUploadsDir(): void + { + if (!file_exists($this->uploadsDir) && !@mkdir($this->uploadsDir)) { + throw new \Exception('Patches upload directory could not be created.'); + } + } + + /** + * Get the directory in which patches for given bug id should be stored. + */ + private function getPatchDir(int $bugId, string $name): string + { + return $this->uploadsDir.'/p'.$bugId.'/'.$name; + } + + /** + * Create the directory in which patches for the given bug id will be stored. + */ + private function createPatchDir(int $bugId, string $name): void + { + $patchDir = $this->getPatchDir($bugId, $name); + $parentDir = dirname($patchDir); + + // Check if patch directory already exists. + if (is_dir($patchDir)) { + return; + } + + // Check if files with same names as directories already exist. + if (is_file($parentDir) || is_file($patchDir)) { + throw new \Exception('Cannot create patch storage for Bug #'.$bugId.', storage directory exists and is not a directory'); + } + + // Create parent directory + if (!file_exists($parentDir) && !@mkdir($parentDir)) { + throw new \Exception('Cannot create patch storage for Bug #'.$bugId); + } + + // Create patch directory + if (!@mkdir($patchDir)) { + throw new \Exception('Cannot create patch storage for Bug #'.$bugId); + } + } + + /** + * Retrieve a unique, ordered patch filename. + */ + private function newPatchFileName(int $bugId, string $patch, string $developer): int + { + $revision = time(); + + $sql = 'INSERT INTO bugdb_patchtracker + (bugdb_id, patch, revision, developer) VALUES (?, ?, ?, ?) + '; + + try { + $this->dbh->prepare($sql)->execute([$bugId, $patch, $revision, $developer]); + } catch (\Exception $e) { + // Try with another timestamp + try { + $revision++; + $this->dbh->prepare($sql)->execute([$bugId, $patch, $revision, $developer]); + } catch (\Exception $e) { + throw new \Exception('Could not get unique patch file name for bug #'.$bugId.', patch "'.$patch.'"'); + } + } + + return $revision; + } + + /** + * Retrieve the name of the patch file on the system. + */ + private function getPatchFileName(int $revision): string + { + return 'p'.$revision.'.patch.txt'; + } + + /** + * Retrieve the full path to a patch file. + */ + public function getPatchFullpath(int $bugId, string $name, int $revision): string + { + return $this->getPatchDir($bugId, $name).'/'.$this->getPatchFileName($revision); + } + + /** + * Attach a patch to this bug. + */ + public function attach(int $bugId, string $patch, string $name, string $developer, array $obsoletes = []): int + { + $this->uploader->setDir($this->getPatchDir($bugId, $name)); + + if (!is_array($obsoletes)) { + throw new \Exception('Invalid obsoleted patches'); + } + + try { + $revision = $this->newPatchFileName($bugId, $name, $developer); + $this->uploader->setDestinationFileName($this->getPatchFileName($revision)); + } catch (\Exception $e) { + throw new \Exception($e->getMessage()); + } + + try { + $this->createUploadsDir(); + + $this->validatePatchName($name); + + $this->createPatchDir($bugId, $name); + + $this->uploader->upload($patch); + } catch (\Exception $e) { + $this->detach($bugId, $name, $revision); + + throw new \Exception($e->getMessage()); + } + + $newObsoletes = []; + foreach ($obsoletes as $obsoletePatch) { + // The none option in form. + if (!$obsoletePatch) { + continue; + } + + $obsoletePatch = explode('#', $obsoletePatch); + + if (count($obsoletePatch) != 2) { + continue; + } + + if (file_exists($this->getPatchFullpath($bugId, $obsoletePatch[0], $obsoletePatch[1]))) { + $newObsoletes[] = $obsoletePatch; + } + } + + foreach ($newObsoletes as $obsolete) { + $this->obsoletePatch($bugId, $name, $revision, $obsolete[0], $obsolete[1]); + } + + return $revision; + } + + /** + * Validate patch name. + */ + private function validatePatchName(string $name): void + { + if (!preg_match('/^[\w\-\.]+\z/', $name) || strlen($name) > 80) { + throw new \Exception('Invalid patch name "'.htmlspecialchars($name, ENT_QUOTES).'"'); + } + } + + /** + * Remove a patch revision from this bug. + */ + private function detach(int $bugId, string $name, int $revision): void + { + $sql = 'DELETE FROM bugdb_patchtracker + WHERE bugdb_id = ? AND patch = ? AND revision = ? + '; + + $this->dbh->prepare($sql)->execute([$bugId, $name, $revision]); + + @unlink($this->getPatchFullpath($bugId, $name, $revision)); + } + + /** + * Make patch obsolete by new patch. This create a link to an obsolete patch + * from the new one. + */ + private function obsoletePatch(int $bugId, string $name, int $revision, string $obsoleteName, int $obsoleteRevision): void + { + $sql = 'INSERT INTO bugdb_obsoletes_patches VALUES (?, ?, ?, ?, ?)'; + + $this->dbh->prepare($sql)->execute([$bugId, $name, $revision, $obsoleteName, $obsoleteRevision]); + } +} diff --git a/src/Utils/Uploader.php b/src/Utils/Uploader.php new file mode 100644 index 00000000..6591b78a --- /dev/null +++ b/src/Utils/Uploader.php @@ -0,0 +1,219 @@ +maxFileSize = $maxFileSize; + } + + /** + * Set allowed file extension without leading dot. For example, 'tgz'. + */ + public function setValidExtension(string $validExtension): void + { + $this->validExtension = $validExtension; + } + + /** + * Set array of valid media types. + */ + public function setValidMediaTypes(array $validMediaTypes): void + { + $this->validMediaTypes = $validMediaTypes; + } + + /** + * Set destination directory. + */ + public function setDir(string $dir): void + { + $this->dir = $dir; + } + + /** + * Set the destination file name. + */ + public function setDestinationFileName(string $destinationFileName): void + { + $this->destinationFileName = $destinationFileName; + } + + /** + * Upload file. + */ + public function upload(string $key): string + { + $files = isset($_FILES[$key]) ? $_FILES[$key] : []; + + // Check if uploaded file size exceeds the ini post_max_size directive. + if( + empty($_FILES) + && empty($_POST) + && isset($_SERVER['REQUEST_METHOD']) + && strtolower($_SERVER['REQUEST_METHOD']) === 'post' + ) { + $max = ini_get('post_max_size'); + throw new \Exception('Error on upload: Exceeded POST content length server limit of '.$max); + } + + // Some other upload error happened + if (empty($files) || $files['error'] !== UPLOAD_ERR_OK) { + throw new \Exception('Error on upload: Something went wrong. Error code: '.$files['error']); + } + + // Be sure we're dealing with an upload + if ($this->isUploadedFile($files['tmp_name']) === false) { + throw new \Exception('Error on upload: Invalid file definition'); + } + + // Check file extension + $uploadedName = $files['name']; + $ext = $this->getFileExtension($uploadedName); + if (isset($this->validExtension) && $ext !== $this->validExtension) { + throw new \Exception('Error on upload: Invalid file extension. Should be .'.$this->validExtension); + } + + // Check file size + if ($files['size'] > $this->maxFileSize) { + throw new \Exception('Error on upload: Exceeded file size limit '.$this->maxFileSize.' bytes'); + } + + // Check zero length file size + if (!$files['size']) { + throw new \Exception('Error on upload: Zero-length patches are not allowed'); + } + + // Check media type + $type = $this->getMediaType($files['tmp_name']); + if (isset($this->validMediaTypes) && !in_array($type, $this->validMediaTypes)) { + throw new \Exception('Error: Uploaded patch file must be text file + (save as e.g. "patch.txt" or "package.diff") + (detected "'.htmlspecialchars($type, ENT_QUOTES).'")' + ); + } + + // Rename the uploaded file + $destination = $this->dir.'/'.$this->destinationFileName; + + // Move uploaded file to final destination + if (!$this->moveUploadedFile($files['tmp_name'], $destination)) { + throw new \Exception('Error on upload: Something went wrong'); + } + + return $destination; + } + + /** + * Checks if given file has been uploaded via POST method. This is wrapped + * into a separate method for convenience of testing it via phpunit and using + * a mock. + */ + protected function isUploadedFile(string $file): bool + { + return is_uploaded_file($file); + } + + /** + * Move uploaded file to destination. This method is wrapping PHP function + * to allow testing with PHPUnit and creating a mock object. + */ + protected function moveUploadedFile(string $source, string $destination): bool + { + return move_uploaded_file($source, $destination); + } + + /** + * Rename file to a unique name. + */ + protected function renameFile(string $filename): ?string + { + $ext = $this->getFileExtension($filename); + + $rand = uniqid(rand()); + + $i = 0; + while (true) { + $newName = $rand.$i.'.'.$ext; + + if (!file_exists($this->dir.'/'.$newName)) { + return $newName; + } + + $i++; + } + } + + /** + * Returns file extension without a leading dot. + */ + protected function getFileExtension(string $filename): string + { + return strtolower(substr($filename, strripos($filename, '.') + 1)); + } + + /** + * Guess file media type (formerly known as MIME type) using the fileinfo + * extension. If fileinfo extension is not installed fallback to plain text + * type. + */ + protected function getMediaType(string $file): string + { + // If fileinfo extension is not available it defaults to text/plain. + if (!class_exists('finfo')) { + return 'text/plain'; + } + + $finfo = new \finfo(FILEINFO_MIME); + + if (!$finfo) { + throw new \Exception('Error: Opening fileinfo database failed.'); + } + + // Get type for a specific file + $type = $finfo->file($file); + + // Remove the charset part + $mediaType = explode(';', $type); + + return isset($mediaType[0]) ? $mediaType[0] : 'text/plain'; + } +} diff --git a/templates/addpatch.php b/templates/addpatch.php index 52857890..faad8c8e 100644 --- a/templates/addpatch.php +++ b/templates/addpatch.php @@ -61,15 +61,13 @@
      diff --git a/tests/Utils/UploaderTest.php b/tests/Utils/UploaderTest.php new file mode 100644 index 00000000..982cc7f9 --- /dev/null +++ b/tests/Utils/UploaderTest.php @@ -0,0 +1,54 @@ +getMockBuilder(Uploader::class) + ->setMethods(['isUploadedFile', 'moveUploadedFile']) + ->getMock(); + + $uploader->expects($this->once()) + ->method('isUploadedFile') + ->will($this->returnValue(true)); + + $uploader->expects($this->once()) + ->method('moveUploadedFile') + ->will($this->returnValue(true)); + + $uploader->setMaxFileSize(100 * 1024); + $uploader->setValidExtension($validExtension); + $uploader->setDir(__DIR__.'/../../var/uploads'); + $tmpFile = $uploader->upload('uploaded'); + + $this->assertNotNull($tmpFile); + } + + public function filesProvider() + { + return [ + [ + 'txt', + [ + 'name' => 'patch.txt', + 'tmp_name' => $this->fixturesDirectory.'/patch.txt', + 'size' => filesize($this->fixturesDirectory.'/patch.txt'), + 'error' => UPLOAD_ERR_OK, + ] + ], + ]; + } +} diff --git a/tests/fixtures/files/patch.txt b/tests/fixtures/files/patch.txt new file mode 100644 index 00000000..d675fa44 --- /dev/null +++ b/tests/fixtures/files/patch.txt @@ -0,0 +1 @@ +foo bar diff --git a/www/bug.php b/www/bug.php index c676d139..bb87dc73 100644 --- a/www/bug.php +++ b/www/bug.php @@ -1,6 +1,8 @@ listPatches($bug_id); + $p = $patchRepository->findAllByBugId($bug_id); $revs = []; echo "

      Patches

      \n"; @@ -1071,7 +1074,7 @@ foreach ($revs as $name => $revisions) { - $obsolete = $patches->getObsoletingPatches($bug_id, $name, $revisions[0][0]); + $obsolete = $obsoletePatchRepository->findObsoletingPatches($bug_id, $name, $revisions[0][0]); $style = !empty($obsolete) ? ' style="background-color: yellow; text-decoration: line-through;" ' : ''; $url_name = urlencode($name); $clean_name = clean($name); @@ -1080,7 +1083,7 @@ echo <<< OUTPUT {$clean_name} -(last revision {$formatted_date}) by {$submitter}) +(last revision {$formatted_date} by {$submitter})
      OUTPUT; } diff --git a/www/patch-add.php b/www/patch-add.php index 0b1db805..763a011f 100644 --- a/www/patch-add.php +++ b/www/patch-add.php @@ -1,10 +1,17 @@ listPatches($bug_id); + $patches = $patchRepository->findAllByBugId($bug_id); $errors[] = 'No patch name entered'; include "{$ROOT_DIR}/templates/addpatch.php"; exit; @@ -90,24 +94,23 @@ } if (count($errors)) { - throw new Exception(''); + throw new \Exception(''); } - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $e = $patchinfo->attach($bug_id, 'patch', $patch_name, $email, $_POST['obsoleted']); - PEAR::popErrorHandling(); - - if (PEAR::isError($e)) { - $patches = $patchinfo->listPatches($bug_id); + try { + $revision = $patchTracker->attach($bug_id, 'patch', $patch_name, $email, $_POST['obsoleted']); + } catch (\Exception $e) { + $patches = $patchRepository->findAllByBugId($bug_id); $errors[] = $e->getMessage(); - $errors[] = 'Could not attach patch "' . htmlspecialchars($patch_name) . '" to Bug #' . $bug_id; + $errors[] = 'Could not attach patch "'.htmlspecialchars($patch_name).'" to Bug #'.$bug_id; include "{$ROOT_DIR}/templates/addpatch.php"; + exit; } - redirect("patch-display.php?bug={$bug_id}&patch={$patch_name_url}&revision={$e}"); - } catch (Exception $e) { - $patches = $patchinfo->listPatches($bug_id); + redirect("patch-display.php?bug={$bug_id}&patch={$patch_name_url}&revision={$revision}"); + } catch (\Exception $e) { + $patches = $patchRepository->findAllByBugId($bug_id); include "{$ROOT_DIR}/templates/addpatch.php"; exit; } @@ -115,27 +118,27 @@ $email = $auth_user->email; } - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $e = $patchinfo->attach($bug_id, 'patch', $patch_name, $auth_user->email, $_POST['obsoleted']); - PEAR::popErrorHandling(); - if (PEAR::isError($e)) { - $patches = $patchinfo->listPatches($bug_id); - $errors = [$e->getMessage(), - 'Could not attach patch "' . - htmlspecialchars($patch_name) . - '" to Bug #' . $bug_id]; + try { + $revision = $patchTracker->attach($bug_id, 'patch', $patch_name, $auth_user->email, $_POST['obsoleted']); + } catch (\Exception $e) { + $patches = $patchRepository->findAllByBugId($bug_id); + $errors = [ + $e->getMessage(), + 'Could not attach patch "'.htmlspecialchars($patch_name, ENT_QUOTES).'" to Bug #'.$bug_id + ]; include "{$ROOT_DIR}/templates/addpatch.php"; + exit; } // Add a comment to the bug report. - $patch_url = "{$site_method}://{$site_url}{$basedir}/patch-display.php?bug={$bug_id}&patch={$patch_name_url}&revision={$e}"; + $patch_url = "{$site_method}://{$site_url}{$basedir}/patch-display.php?bug={$bug_id}&patch={$patch_name_url}&revision={$revision}"; $text = <<email, $text, 4, $bug_id); - $patches = $patchinfo->listPatches($bug_id); + $patches = $patchRepository->findAllByBugId($bug_id); $errors = []; include "{$ROOT_DIR}/templates/patchadded.php"; exit; } $email = isset($_GET['email']) ? $_GET['email'] : ''; -$patches = $patchinfo->listPatches($bug_id); +$patches = $patchRepository->findAllByBugId($bug_id); include "{$ROOT_DIR}/templates/addpatch.php"; diff --git a/www/patch-display.php b/www/patch-display.php index 831e340a..bcf82f5f 100644 --- a/www/patch-display.php +++ b/www/patch-display.php @@ -1,10 +1,20 @@ listRevisions($buginfo['id'], $patch_name); + $revisions = $patchRepository->findRevisions($buginfo['id'], $patch_name); if (isset($revisions[0])) { $revision = $revisions[0]['revision']; } } - $path = $patchinfo->getPatchFullpath($bug_id, $patch_name, $revision); + $path = $patchTracker->getPatchFullpath($bug_id, $patch_name, $revision); if (!file_exists($path)) { response_header('Error :: no such patch/revision'); display_bug_error('Invalid patch/revision specified'); @@ -73,9 +80,10 @@ readfile($path); exit; } - $patchcontents = $patchinfo->getPatch($buginfo['id'], $patch_name, $revision); - if (PEAR::isError($patchcontents)) { + try { + $patchcontents = $patchRepository->getPatchContents($buginfo['id'], $patch_name, $revision); + } catch (\Exception $e) { response_header('Error :: Cannot retrieve patch'); display_bug_error('Internal error: Invalid patch/revision specified (is in database, but not in filesystem)'); response_footer(); @@ -83,17 +91,17 @@ } $package_name = $buginfo['package_name']; - $handle = $patchinfo->getDeveloper($bug_id, $patch_name, $revision); - $obsoletedby = $patchinfo->getObsoletingPatches($bug_id, $patch_name, $revision); - $obsoletes = $patchinfo->getObsoletePatches($bug_id, $patch_name, $revision); - $patches = $patchinfo->listPatches($bug_id); - $revisions = $patchinfo->listRevisions($bug_id, $patch_name); + $handle = $patchRepository->findDeveloper($bug_id, $patch_name, $revision); + $obsoletedby = $obsoletePatchRepository->findObsoletingPatches($bug_id, $patch_name, $revision); + $obsoletes = $obsoletePatchRepository->findObsoletePatches($bug_id, $patch_name, $revision); + $patches = $patchRepository->findAllByBugId($bug_id); + $revisions = $patchRepository->findRevisions($bug_id, $patch_name); response_header("Bug #{$bug_id} :: Patches"); include "{$ROOT_DIR}/templates/listpatches.php"; if (isset($_GET['diff']) && $_GET['diff'] && isset($_GET['old']) && is_numeric($_GET['old'])) { - $old = $patchinfo->getPatchFullpath($bug_id, $patch_name, $_GET['old']); + $old = $patchTracker->getPatchFullpath($bug_id, $patch_name, $_GET['old']); $new = $path; if (!realpath($old) || !realpath($new)) { response_header('Error :: Cannot retrieve patch'); @@ -116,7 +124,7 @@ exit; } -$patches = $patchinfo->listPatches($bug_id); +$patches = $patchTracker->listPatches($bug_id); response_header("Bug #{$bug_id} :: Patches"); include "{$ROOT_DIR}/templates/listpatches.php"; response_footer(); diff --git a/www/report.php b/www/report.php index c2a52f4a..9cd6414f 100644 --- a/www/report.php +++ b/www/report.php @@ -1,6 +1,8 @@ attach($cid, 'patchfile', $_POST['in']['patchname'], $_POST['in']['handle'], []); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($patchrevision)) { + $uploader = new Uploader(); + $tracker = new PatchTracker($dbh, $uploader); + + try { + $developer = !empty($_POST['in']['handle']) ? $_POST['in']['handle'] : $_POST['in']['email']; + $patchrevision = $tracker->attach($cid, 'patchfile', $_POST['in']['patchname'], $developer, []); + } catch (\Exception $e) { $redirectToPatchAdd = true; } } From 4d51010f8c2e71a0d28a01de41f5358aec97e615 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 9 Dec 2018 21:18:30 +0100 Subject: [PATCH 145/277] Simplify GitHub pull request form The form for attaching pull requests doesn't need to upload files. --- templates/addghpull.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/templates/addghpull.php b/templates/addghpull.php index fb06dd13..1102bf41 100644 --- a/templates/addghpull.php +++ b/templates/addghpull.php @@ -10,8 +10,7 @@
    • The pull request must be opened against a PHP project on GitHub
    • Choose a meaningful request name (i.e. include bug id and title)
    • -
      - + Date: Sun, 9 Dec 2018 22:01:39 +0100 Subject: [PATCH 146/277] Add type declarations to Captcha class --- src/Utils/Captcha.php | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Utils/Captcha.php b/src/Utils/Captcha.php index 01377aaf..b56019a4 100644 --- a/src/Utils/Captcha.php +++ b/src/Utils/Captcha.php @@ -10,11 +10,13 @@ class Captcha { /** * First operand. + * @var int */ private $first; /** * Last operand. + * @var int */ private $last; @@ -34,6 +36,7 @@ class Captcha /** * Current operation. + * @var string */ private $operation; @@ -48,7 +51,7 @@ public function __construct() /** * Set random operands values and operation. */ - public function randomize() + public function randomize(): void { $this->setFirst(rand(1, self::MAX)); $this->setLast(rand(1, self::MAX)); @@ -59,7 +62,7 @@ public function randomize() * First operand number setter to override default random pick. Defined as a * separate method for convenience when unit testing. */ - public function setFirst($number) + public function setFirst(int $number): void { $this->first = $number; } @@ -68,7 +71,7 @@ public function setFirst($number) * Last operand number setter to override default random pick. Defined as a * separate method for convenience when unit testing. */ - public function setLast($number) + public function setLast(int $number): void { $this->last = $number; } @@ -76,7 +79,7 @@ public function setLast($number) /** * Set the operation. If provided operation is invalid it falls back to addition. */ - public function setOperation($operation) + public function setOperation(string $operation): void { $this->operation = in_array($operation, self::OPERATIONS) ? $operation : 'addition'; } @@ -84,7 +87,7 @@ public function setOperation($operation) /** * Get current question equation string for displaying it to the user. */ - public function getQuestion() + public function getQuestion(): string { $this->sortOperands(); @@ -97,7 +100,7 @@ public function getQuestion() /** * The correct current answer of the given equation question. */ - public function getAnswer() + public function getAnswer(): int { $this->sortOperands(); @@ -109,7 +112,7 @@ public function getAnswer() * operand first. With this, negative results are omitted for simplicity and * possible better user experience. */ - private function sortOperands() + private function sortOperands(): void { $first = $this->first; $last = $this->last; @@ -123,7 +126,7 @@ private function sortOperands() /** * Addition of two operands. */ - private function addition($first, $last) + private function addition(int $first, int $last): int { return $first + $last; } @@ -131,7 +134,7 @@ private function addition($first, $last) /** * Subtraction of two operands. */ - private function subtraction($first, $last) + private function subtraction(int $first, int $last): int { return $first - $last; } From be3c259ecaae28db43a3a8c30338a876f112e36e Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 10 Dec 2018 02:08:58 +0100 Subject: [PATCH 147/277] Add .editorconfig file This patch adds initial EditorConfig [1] file with sensible defaults specific for future PHP code, HTML templates, and possible other file types. It helps formating code and new files in editors and IDEs that utilize EditorConfig settings. Current state of EditorConfig provides setting identation style and level, new lines types, files encodings, and similar basic files and code configurations. [1] https://editorconfig.org --- .editorconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..ada43e26 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# https://editorconfig.org/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +tab_width = 4 +trim_trailing_whitespace = true +insert_final_newline = true From 001a1dbe21cf6b61c6cc8d45afaa2d5c8cb7c6e7 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 10 Dec 2018 02:20:20 +0100 Subject: [PATCH 148/277] Add directory structure to README --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index 7e353d0d..799e64b5 100644 --- a/README.md +++ b/README.md @@ -37,3 +37,35 @@ installing dependencies by running `phpunit`: ```bash phpunit ``` + +## Directory structure + +Source code of this application is structured in the following directories: + +```bash +/ + ├─ .git/ # Git configuration and source directory + └─ include/ # Application helper functions and configuration + ├─ prepend.php # Autoloader, DB connection, container, app initialization + └─ ... + └─ scripts/ # Command line development tools and scripts + ├─ cron/ # Various systems scripts to run periodically on the server + └─ ... + ├─ sql/ # Database schema and fixtures + ├─ src/ # Application source code classes + ├─ templates/ # Application templates + ├─ tests/ # Application automated tests + ├─ uploads/ # Uploaded patch files + ├─ vendor/ # Dependencies generated by Composer + └─ www/ # Publicly accessible directory for online bugs.php.net + ├─ css/ # Stylesheets + ├─ images/ # Images + ├─ js/ # JavaScript assets + └─ ... + ├─ composer.json # Composer dependencies and project meta definition + ├─ composer.lock # Dependencies versions currently installed + ├─ local_config.php # Application configuration + ├─ local_config.php.sample # Distributed configuration example + ├─ phpunit.xml.dist # PHPUnit's default XML configuration + └─ ... +``` From 0173592272937e4c8c480da9932bbd418aef69eb Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 11 Dec 2018 02:04:27 +0100 Subject: [PATCH 149/277] Add contributing procedure --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 799e64b5..92d59553 100644 --- a/README.md +++ b/README.md @@ -69,3 +69,36 @@ Source code of this application is structured in the following directories: ├─ phpunit.xml.dist # PHPUnit's default XML configuration └─ ... ``` + +## Contributing + +Issues with the application and new feature requests can be reported to +[bugs.php.net](https://bugs.php.net) and discussed by sending message to the +[webmaster mailing list](http://news.php.net/php.webmaster) to the address +php-webmaster@lists.php.net. + +Application source code is located in the +[git.php.net](https://git.php.net/?p=web/bugs.git) repository. + +Contributions can be done by forking the [GitHub mirror](https://github.com/php/web-bugs) +repository and sending a pull request. + +```bash +git clone git@github.com:your-username/web-bugs +cd web-bugs +git checkout -b patch-1 +git add . +git commit -m "Describe changes" +git push origin patch-1 +``` + +A good practice is to also set the upstream remote in case the upstream master +branch updates. This way your master branch will track remote upstream master +branch of the root repository. + +```bash +git checkout master +git remote add upstream git://github.com/php/web-bugs +git config branch.master.remote upstream +git pull --rebase +``` From 9b248f9ed6a53dcc6400edbc1e0728c25a44cf89 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 10 Dec 2018 01:41:56 +0100 Subject: [PATCH 150/277] Refactor get_pseudo_packages() to repository class Changes: - get_pseudo_packages() function is moved to its own repository class. - Database::queryAll() removed since it is not used and the method arguments don't match the number of used arguments anymore - Project types configuration is moved to repository class for now. - Some unused items removed - Some template changes and show_project_options() helper function integrated in the view layer directly since it is used in a simplified way. --- include/functions.php | 103 ---------------------- include/query.php | 5 +- src/Database/Database.php | 5 -- src/Repository/PackageRepository.php | 122 +++++++++++++++++++++++++++ www/bug.php | 4 +- www/lstats.php | 6 +- www/patch-display.php | 2 - www/report.php | 9 +- www/search.php | 11 ++- www/stats.php | 1 - 10 files changed, 147 insertions(+), 121 deletions(-) create mode 100644 src/Repository/PackageRepository.php diff --git a/include/functions.php b/include/functions.php index 8f35fe22..04ec1bb7 100644 --- a/include/functions.php +++ b/include/functions.php @@ -33,11 +33,6 @@ 'Security' => 'Sec Bug' ]; -$project_types = [ - 'PHP' => 'php', - 'PECL' => 'pecl' -]; - // Used in show_state_options() $state_types = [ 'Open' => 2, @@ -192,70 +187,6 @@ function bugs_authenticate (&$user, &$pw, &$logged_in, &$user_flags) } } -/** - * Fetches pseudo packages from database - * - * @param string $project define what project pseudo packages are returned - * @param bool $return_disabled whether to return read-only items, defaults to true - * - * @return array array of pseudo packages - */ -function get_pseudo_packages($project, $return_disabled = true) -{ - global $dbh, $project_types; - - $pseudo_pkgs = $nodes = $tree = []; - $where = '1=1'; - $project = strtolower($project); - - if ($project !== false && in_array($project, $project_types)) { - $where .= " AND project IN ('', '$project')"; - } - if (!$return_disabled) { - $where.= " AND disabled = 0"; - } - - $data = $dbh->queryAll("SELECT * FROM bugdb_pseudo_packages WHERE $where ORDER BY parent, disabled, id", null, PDO::FETCH_ASSOC); - - // Convert flat array to nested strucutre - foreach ($data as &$node) - { - $node['children'] = []; - $id = $node['id']; - $parent_id = $node['parent']; - $nodes[$id] =& $node; - - if (array_key_exists($parent_id, $nodes)) { - $nodes[$parent_id]['children'][] =& $node; - } else { - $tree[] =& $node; - } - } - - foreach ($tree as $data) - { - if (isset($data['children'])) { - $pseudo_pkgs[$data['name']] = [$data['long_name'], $data['disabled'], []]; - $children = &$pseudo_pkgs[$data['name']][2]; - $long_names = []; - foreach ($data['children'] as $k => $v) { - $long_names[$k] = strtolower($v['long_name']); - } - array_multisort($long_names, SORT_ASC, SORT_STRING, $data['children']); - foreach ($data['children'] as $child) - { - $pseudo_pkgs[$child['name']] = ["{$child['long_name']}", $child['disabled'], null]; - $children[] = $child['name']; - } - - } elseif (!isset($pseudo_pkgs[$data['name']])) { - $pseudo_pkgs[$data['name']] = [$data['long_name'], $data['disabled'], null]; - } - } - - return $pseudo_pkgs; -} - /* Primitive check for SPAM. Add more later. */ function is_spam($string) { @@ -528,40 +459,6 @@ function show_limit_options($limit = 30) echo ">All\n"; } -/** - * Prints bug project
      +
      Solve the problem:
      = ?
      Solve the problem:
      getQuestion()); ?>

      = ?

      getQuestion()); ?>
      +
      diff --git a/www/stats.php b/www/stats.php index 06c1d5f4..8b161c4d 100644 --- a/www/stats.php +++ b/www/stats.php @@ -35,7 +35,6 @@ $pkg_names = []; $all = []; $pseudo = true; -$pseudo_pkgs = get_pseudo_packages($site); if (!array_key_exists($sort_by, $titles)) { $sort_by = 'Open'; From bae5fa6c4428956efd618bf69038f724875b7de5 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Fri, 14 Dec 2018 15:20:09 +0100 Subject: [PATCH 151/277] Trim trailing whitespaces --- include/query.php | 2 +- templates/search-rdf.php | 2 +- www/admin/index.php | 4 ++-- www/bug.php | 6 +++--- www/report.php | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/query.php b/include/query.php index 028f57ae..da996d85 100644 --- a/include/query.php +++ b/include/query.php @@ -58,7 +58,7 @@ if (isset($_GET['cmd']) && $_GET['cmd'] == 'display') { $query = ' - SELECT SQL_CALC_FOUND_ROWS + SELECT SQL_CALC_FOUND_ROWS bugdb.*, TO_DAYS(NOW())-TO_DAYS(bugdb.ts2) AS unchanged, UNIX_TIMESTAMP(ts1) AS submitted, diff --git a/templates/search-rdf.php b/templates/search-rdf.php index 68d3e93d..496ddfa7 100644 --- a/templates/search-rdf.php +++ b/templates/search-rdf.php @@ -2,7 +2,7 @@ header('Content-type: text/xml'); echo ' - query(" - SELECT name, list_email - FROM bugdb_pseudo_packages + SELECT name, list_email + FROM bugdb_pseudo_packages WHERE project = 'php' AND LENGTH(list_email) > 0 ORDER BY list_email diff --git a/www/bug.php b/www/bug.php index 2dbb79c5..502e50a0 100644 --- a/www/bug.php +++ b/www/bug.php @@ -1162,10 +1162,10 @@ function do_comment(nd) { $('#comment_filter > .control.active').removeClass("active"); $(nd).addClass("active"); - + $.cookie('history_tab', nd.id, { expires: 365 }); - - if (nd.id == 'type_all') { + + if (nd.id == 'type_all') { $('#comments_view > .comment:hidden').show('slow'); } else { $('#comments_view > .comment').each(function(i) { diff --git a/www/report.php b/www/report.php index 4ff48692..017cea1a 100644 --- a/www/report.php +++ b/www/report.php @@ -117,7 +117,7 @@ foreach ($possible_duplicates as $row) { $resolution = $dbh->prepare(" - SELECT comment + SELECT comment FROM bugdb_comments WHERE bug = ? ORDER BY id DESC From 3c4fa66c4cae0d09c4cbecc305e99de217ecae49 Mon Sep 17 00:00:00 2001 From: Kalle Sommer Nielsen Date: Sat, 15 Dec 2018 22:17:49 +0100 Subject: [PATCH 152/277] Use closures over create_function() --- include/classes/bug_diff_renderer.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/include/classes/bug_diff_renderer.php b/include/classes/bug_diff_renderer.php index 9fb0b3c1..0071109f 100644 --- a/include/classes/bug_diff_renderer.php +++ b/include/classes/bug_diff_renderer.php @@ -37,19 +37,22 @@ public function _blockHeader($xbeg, $xlen, $ybeg, $ylen) public function _added($lines) { - array_walk($lines, create_function('&$a,$b', '$a=htmlspecialchars($a);')); + self::escapeHTML($lines); + return ' ' . implode("\n ", $lines) . ''; } public function _context($lines) { - array_walk($lines, create_function('&$a,$b', '$a=htmlspecialchars($a);')); + self::escapeHTML($lines); + return "\n" . parent::_context($lines); } public function _deleted($lines) { - array_walk($lines, create_function('&$a,$b', '$a=htmlspecialchars($a);')); + self::escapeHTML($lines); + return ' ' . implode("\n ", $lines) . ''; } @@ -62,4 +65,12 @@ public function render($diff) { return parent::render($this->diff); } + + protected static function escapeHTML(&$lines) + { + array_walk($lines, function(&$a, $b) + { + $a = htmlspecialchars($a); + }); + } } From 8d01322dbf8300cd74e0b654b9a54f641359cdb4 Mon Sep 17 00:00:00 2001 From: Kalle Sommer Nielsen Date: Sat, 15 Dec 2018 22:22:54 +0100 Subject: [PATCH 153/277] WS --- include/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/functions.php b/include/functions.php index 04ec1bb7..a45cda07 100644 --- a/include/functions.php +++ b/include/functions.php @@ -91,7 +91,7 @@ function verify_user_password($user, $pass) return false; } - $_SESSION["user"] = $user; + $_SESSION["user"] = $user; return true; } @@ -148,7 +148,7 @@ function bugs_authenticate (&$user, &$pw, &$logged_in, &$user_flags) } elseif (isset($auth_user) && is_object($auth_user) && $auth_user->handle) { $user = $auth_user->handle; $pw = $auth_user->password; - } + } // Authentication and user level check // User levels are: reader (0), commenter/patcher/etc. (edit = 3), submitter (edit = 2), developer (edit = 1) From d9ada8ec72dd0e39e42b8e7f9c94f6c6699cc0d0 Mon Sep 17 00:00:00 2001 From: Kalle Sommer Nielsen Date: Sat, 15 Dec 2018 22:30:10 +0100 Subject: [PATCH 154/277] PHP 7.0 is EOL --- www/index.php | 1 - 1 file changed, 1 deletion(-) diff --git a/www/index.php b/www/index.php index c7e29dc6..b744d089 100644 --- a/www/index.php +++ b/www/index.php @@ -99,7 +99,6 @@ 'Most recent open bugs (all)' => '&bug_type=All', 'Most recent open bugs (all) with patch or pull request' => '&bug_type=All&patch=Y&pull=Y', 'Most recent open bugs (PHP 5.6)' => '&bug_type=All&phpver=5.6', - 'Most recent open bugs (PHP 7.0)' => '&bug_type=All&phpver=7.0', 'Most recent open bugs (PHP 7.1)' => '&bug_type=All&phpver=7.1', 'Most recent open bugs (PHP 7.2)' => '&bug_type=All&phpver=7.2', 'Most recent open bugs (PHP 7.3)' => '&bug_type=All&phpver=7.3', From 3a9021ab3a0ca634023a73f3c4220b67ba386be0 Mon Sep 17 00:00:00 2001 From: Kalle Sommer Nielsen Date: Sat, 15 Dec 2018 22:47:25 +0100 Subject: [PATCH 155/277] Added include/classes to the directory structure --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 92d59553..47fd7594 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PHP Bug Tracking System +# PHP Bug Tracking System This is a unified bug tracking system for PHP hosted online at [bugs.php.net](https://bugs.php.net). @@ -46,6 +46,7 @@ Source code of this application is structured in the following directories: / ├─ .git/ # Git configuration and source directory └─ include/ # Application helper functions and configuration + ├─ classes/ # PEAR class overrides ├─ prepend.php # Autoloader, DB connection, container, app initialization └─ ... └─ scripts/ # Command line development tools and scripts From 14f8c07aec4285ee376f5f88a64d3449e1e6b6ef Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Fri, 14 Dec 2018 15:22:18 +0100 Subject: [PATCH 156/277] Refactor PDO wrapper Some considerations were raised on the mailing list that this PHP application doesn't need a PDO wrapper at all. Changes: - ::fetchRow() method removed in favor of the vanilla PDOStatement::fetch() - ::fetchAll() override removed in favor of the vanilla PDOStatement::fetchAll() - ::fetchCol() removed since it is not used and is only a wrapper for the PDOStatement::fetchColumn() - PDO fetch_style synced accross the app. When no fetch style is passed the default PDO::FETCH_ASSOC is used as set when connecting to db. - Remove Database::escape() method The custom ::escape() method is a wrapper around PDO::quote() which additionally trims leading and trailing quotes from the string. All this should ideally be done via prepared statements only, except where we can and need to use PDO::quote() this step can be done on the given string or variable at hand directly. - Remove escapeSQL() function The escapeSQL function is a wrapper around the PDO::quote() and is using $dbh from the global scope which is not testable nor good practice further on. Removed and refactored into only PDO::quote() usages on required places. - Remove ::fetchOne() method The fetchOne() method is a simple wrapper around the PDOStatement::fetch() method with very minor tweaks so the usage can be simplified even more. The PDOStatement::fetch(\PDO::FETCH_NUM)[0] will always return either a result from the database column or when row is empty a null. - Probably this should be refactored to the database tables respected repositories further on. - Remove PDO wrapper The app's current goal is to lean on a vanilla PDO wrapper only. Current set of features also don't require additional functionality and extending PDO to a wrapper or create a database abstraction layer yet. --- include/functions.php | 63 +++++++--------------- include/prepend.php | 21 +++++--- include/query.php | 24 +++++---- scripts/cron/email-assigned | 2 +- scripts/cron/no-feedback | 2 +- src/Database/Database.php | 33 ------------ src/Database/Statement.php | 28 +--------- src/Repository/ObsoletePatchRepository.php | 6 +-- src/Repository/PackageRepository.php | 6 +-- src/Repository/PatchRepository.php | 10 ++-- src/Repository/PullRequestRepository.php | 3 +- src/Utils/GitHub.php | 3 +- src/Utils/PatchTracker.php | 5 +- www/admin/index.php | 10 ++-- www/api.php | 2 +- www/bug-pwd-finder.php | 2 +- www/index.php | 2 +- www/lstats.php | 4 +- www/report.php | 2 +- www/stats.php | 4 +- www/vote.php | 4 +- 21 files changed, 80 insertions(+), 156 deletions(-) delete mode 100644 src/Database/Database.php diff --git a/include/functions.php b/include/functions.php index a45cda07..6206db3a 100644 --- a/include/functions.php +++ b/include/functions.php @@ -278,30 +278,6 @@ function spam_protect($txt, $format = 'html') return strtr($txt, $translate); } -/** - * Escape strings so they can be used as literals in queries - * - * @param string|array $in data to be sanitized. If it's an array, each element is sanitized. - * - * @return string|array the sanitized data - * - * @see oneof(), field(), txfield() - */ -function escapeSQL($in) -{ - global $dbh; - - if (is_array($in)) { - $out = []; - foreach ($in as $key => $value) { - $out[$key] = $dbh->escape($value); - } - return $out; - } else { - return $dbh->escape($in); - } -} - /** * Goes through each variable submitted and returns the value * from the first variable which has a non-empty value @@ -312,7 +288,7 @@ function escapeSQL($in) * * @return mixed the value, if any * - * @see escapeSQL(), field(), txfield() + * @see field(), txfield() */ function oneof() { @@ -334,7 +310,7 @@ function oneof() * * @return mixed the data requested * - * @see escapeSQL(), oneof(), txfield() + * @see oneof(), txfield() */ function field($n) { @@ -1075,13 +1051,13 @@ function get_old_comments($bug_id, $all = 0) // skip the most recent unless the caller wanted all comments if (!$all) { - $row = $res->fetchRow(PDO::FETCH_NUM); + $row = $res->fetch(\PDO::FETCH_NUM); if (!$row) { return ''; } } - while (($row = $res->fetchRow(PDO::FETCH_NUM)) && strlen($output) < $max_message_length && $count++ < $max_comments) { + while (($row = $res->fetch(\PDO::FETCH_NUM)) && strlen($output) < $max_message_length && $count++ < $max_comments) { $email = spam_protect($row[1], 'text'); $output .= "[{$row[0]}] {$email}\n\n{$row[2]}\n\n{$divider}\n"; } @@ -1091,7 +1067,7 @@ function get_old_comments($bug_id, $all = 0) if (!$res) { return $output; } - $row = $res->fetchRow(PDO::FETCH_NUM); + $row = $res->fetch(\PDO::FETCH_NUM); if (!$row) { return $output; } @@ -1256,7 +1232,7 @@ function get_package_mail($package_name, $bug_id = false, $bug_type = 'Bug') WHERE name = ? ')->execute([$package_name]); - list($list_email, $project) = $res->fetchRow(); + list($list_email, $project) = $res->fetch(\PDO::FETCH_NUM); if ($project == 'pecl') { $mailfrom = 'pecl-dev@lists.php.net'; @@ -1270,7 +1246,7 @@ function get_package_mail($package_name, $bug_id = false, $bug_type = 'Bug') } else { // Get the maintainers handle if ($project == 'pecl') { - $handles = $dbh->prepare("SELECT GROUP_CONCAT(handle) FROM bugdb_packages_maintainers WHERE package_name = ?")->execute([$package_name])->fetchOne(); + $handles = $dbh->prepare("SELECT GROUP_CONCAT(handle) FROM bugdb_packages_maintainers WHERE package_name = ?")->execute([$package_name])->fetch(\PDO::FETCH_NUM)[0]; if ($handles) { foreach (explode(',', $handles) as $handle) { @@ -1290,7 +1266,7 @@ function get_package_mail($package_name, $bug_id = false, $bug_type = 'Bug') if ($bug_id) { $bug_id = (int) $bug_id; - $assigned = $dbh->prepare("SELECT assign FROM bugdb WHERE id= ? ")->execute([$bug_id])->fetchOne(); + $assigned = $dbh->prepare("SELECT assign FROM bugdb WHERE id= ? ")->execute([$bug_id])->fetch(\PDO::FETCH_NUM)[0]; if ($assigned) { $assigned .= '@php.net'; if ($assigned && !in_array($assigned, $to)) { @@ -1315,6 +1291,8 @@ function get_package_mail($package_name, $bug_id = false, $bug_type = 'Bug') */ function format_search_string($search, $boolean_search = false) { + global $dbh; + // Function will be updated to make results more relevant. // Quick hack for indicating ignored words. $min_word_len=3; @@ -1337,15 +1315,15 @@ function format_search_string($search, $boolean_search = false) foreach ($used as $word) { $newsearch .= "+$word "; } - return [" AND MATCH (bugdb.email,sdesc,ldesc) AGAINST ('" . escapeSQL($newsearch) . "' IN BOOLEAN MODE)", $ignored]; + return [" AND MATCH (bugdb.email,sdesc,ldesc) AGAINST (" . $dbh->quote($newsearch) . " IN BOOLEAN MODE)", $ignored]; // allow custom boolean search (raw) } elseif ($boolean_search === 2) { - return [" AND MATCH (bugdb.email,sdesc,ldesc) AGAINST ('" . escapeSQL($search) . "' IN BOOLEAN MODE)", $ignored]; + return [" AND MATCH (bugdb.email,sdesc,ldesc) AGAINST (" . $dbh->quote($search) . " IN BOOLEAN MODE)", $ignored]; } } // require any of the words (any) - return [" AND MATCH (bugdb.email,sdesc,ldesc) AGAINST ('" . escapeSQL($search) . "')", $ignored]; + return [" AND MATCH (bugdb.email,sdesc,ldesc) AGAINST (" . $dbh->quote($search) . ")", $ignored]; } /** @@ -1415,7 +1393,6 @@ function unsubscribe($bug_id, $hash) { global $dbh; - $hash = escapeSQL($hash); $bug_id = (int) $bug_id; $query = " @@ -1424,7 +1401,7 @@ function unsubscribe($bug_id, $hash) WHERE bug_id = ? AND unsubscribe_hash = ? LIMIT 1 "; - $sub = $dbh->prepare($query)->execute([$bug_id, $hash])->fetch(PDO::FETCH_ASSOC); + $sub = $dbh->prepare($query)->execute([$bug_id, $hash])->fetch(); if (!$sub) { return false; @@ -1458,8 +1435,8 @@ function get_resolve_reasons($project = false) $where = ''; if ($project !== false) { - $project = escapeSQL($project); - $where.= "WHERE (project = '{$project}' OR project = '')"; + $project = $dbh->quote($project); + $where.= "WHERE (project = {$project} OR project = '')"; } $resolves = $variations = []; @@ -1467,7 +1444,7 @@ function get_resolve_reasons($project = false) if (!$res) { throw new Exception("SQL Error in get_resolve_reasons"); } - while ($row = $res->fetchRow(PDO::FETCH_ASSOC)) { + while ($row = $res->fetch()) { if (!empty($row['package_name'])) { $variations[$row['name']][$row['package_name']] = $row['message']; } else { @@ -1502,7 +1479,7 @@ function bugs_get_bug($bug_id) WHERE b.id = ? GROUP BY bug'; - return $dbh->prepare($query)->execute([$bug_id])->fetchRow(PDO::FETCH_ASSOC); + return $dbh->prepare($query)->execute([$bug_id])->fetch(); } /** @@ -1522,7 +1499,7 @@ function bugs_get_bug_comments($bug_id) WHERE c.bug = ? GROUP BY c.id ORDER BY c.ts "; - return $dbh->prepare($query)->execute([$bug_id])->fetchAll(PDO::FETCH_ASSOC); + return $dbh->prepare($query)->execute([$bug_id])->fetchAll(); } /** @@ -1562,7 +1539,7 @@ function verify_bug_passwd($bug_id, $passwd) { global $dbh; - return (bool) $dbh->prepare('SELECT 1 FROM bugdb WHERE id = ? AND passwd = ?')->execute([$bug_id, $passwd])->fetchOne(); + return (bool) $dbh->prepare('SELECT 1 FROM bugdb WHERE id = ? AND passwd = ?')->execute([$bug_id, $passwd])->fetch(\PDO::FETCH_NUM)[0]; } /** diff --git a/include/prepend.php b/include/prepend.php index 9e03d698..388ebb17 100644 --- a/include/prepend.php +++ b/include/prepend.php @@ -1,7 +1,7 @@ \PDO::ERRMODE_EXCEPTION, - \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, - \PDO::ATTR_EMULATE_PREPARES => false, -]); +// Database connection with vanilla PDO to understand app architecture in no time +$dbh = new \PDO( + 'mysql:host='.$site_data['db_host'].';dbname='.$site_data['db'].';charset=utf8', + $site_data['db_user'], + $site_data['db_pass'], + [ + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, + \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, + \PDO::ATTR_EMULATE_PREPARES => false, + \PDO::ATTR_STATEMENT_CLASS => [Statement::class], + ] +); // Last Updated.. $tmp = filectime($_SERVER['SCRIPT_FILENAME']); diff --git a/include/query.php b/include/query.php index da996d85..51ed8073 100644 --- a/include/query.php +++ b/include/query.php @@ -88,7 +88,8 @@ if (!empty($package_name)) { $where_clause .= ' AND bugdb.package_name'; if (count($package_name) > 1) { - $where_clause .= " IN ('" . join("', '", escapeSQL($package_name)) . "')"; + $package_name = array_map([$dbh, 'quote'], $package_name); + $where_clause .= " IN (" . join(", ", $package_name) . ")"; } else { $where_clause .= ' = ' . $dbh->quote($package_name[0]); } @@ -97,7 +98,8 @@ if (!empty($package_nname)) { $where_clause .= ' AND bugdb.package_name'; if (count($package_nname) > 1) { - $where_clause .= " NOT IN ('" . join("', '", escapeSQL($package_nname)) . "')"; + $package_nname = array_map([$dbh, 'quote'], $package_nname); + $where_clause .= " NOT IN (" . join(", ", $package_nname) . ")"; } else { $where_clause .= ' <> ' . $dbh->quote($package_nname[0]); } @@ -169,19 +171,19 @@ } if ($php_os != '') { - $where_clause .= " AND bugdb.php_os {$php_os_not} LIKE '%" . $dbh->escape($php_os) . "%'"; + $where_clause .= " AND bugdb.php_os {$php_os_not} LIKE " . $dbh->quote('%'.$php_os.'%'); } if ($phpver != '') { - $where_clause .= " AND bugdb.php_version LIKE '" . $dbh->escape($phpver) . "%'"; + $where_clause .= " AND bugdb.php_version LIKE " . $dbh->quote($phpver.'%'); } if ($project != '') { - $where_clause .= " AND EXISTS (SELECT 1 FROM bugdb_pseudo_packages b WHERE b.name = bugdb.package_name AND b.project = '". $dbh->escape($project) ."' LIMIT 1)"; + $where_clause .= " AND EXISTS (SELECT 1 FROM bugdb_pseudo_packages b WHERE b.name = bugdb.package_name AND b.project = ". $dbh->quote($project) ." LIMIT 1)"; } if ($cve_id != '') { - $where_clause .= " AND bugdb.cve_id {$cve_id_not} LIKE '" . $dbh->escape($cve_id) . "%'"; + $where_clause .= " AND bugdb.cve_id {$cve_id_not} LIKE " . $dbh->quote($cve_id.'%'); } /* A search for patch&pull should be (patch or pull) */ @@ -213,12 +215,14 @@ if ($pseudo = array_intersect(array_keys($pseudo_pkgs), $package_name)) { $where_clause .= " OR bugdb.package_name"; if (count($pseudo) > 1) { - $where_clause .= " IN ('" . join("', '", escapeSQL($pseudo)) . "')"; + $pseudo = array_map([$dbh, 'quote'], $pseudo); + $where_clause .= " IN (" . join(", ", $pseudo) . ")"; } else { - $where_clause .= " = '" . implode('', escapeSQL($pseudo)) . "'"; + $where_clause .= " = " . $dbh->quote(reset($pseudo)); } } else { - $where_clause .= " OR bugdb.package_name IN ('" . join("', '", escapeSQL(array_keys($pseudo_pkgs))) . "')"; + $items = array_map([$dbh, 'quote'], array_keys($pseudo_pkgs)); + $where_clause .= " OR bugdb.package_name IN (" . join(", ", $items) . ")"; } $query .= "$where_clause )"; @@ -269,7 +273,7 @@ try { $result = $dbh->prepare($query)->execute()->fetchAll(); $rows = count($result); - $total_rows = $dbh->prepare('SELECT FOUND_ROWS()')->execute()->fetchOne(); + $total_rows = $dbh->prepare('SELECT FOUND_ROWS()')->execute()->fetch(\PDO::FETCH_NUM)[0]; } catch (Exception $e) { $errors[] = 'Invalid query: ' . $e->getMessage(); } diff --git a/scripts/cron/email-assigned b/scripts/cron/email-assigned index 6257c521..c1977d49 100755 --- a/scripts/cron/email-assigned +++ b/scripts/cron/email-assigned @@ -17,7 +17,7 @@ $sql = "SELECT id, package_name, bug_type, sdesc, status, assign, UNIX_TIMESTAMP $res = $dbh->query($sql); // Gather up the data -while ($row = $res->fetchRow(PDO::FETCH_ASSOC)) { +while ($row = $res->fetch()) { $data[$row['assign']][] = $row; } diff --git a/scripts/cron/no-feedback b/scripts/cron/no-feedback index a32b9551..e3154f8d 100755 --- a/scripts/cron/no-feedback +++ b/scripts/cron/no-feedback @@ -24,7 +24,7 @@ if ($dbh) WHERE status = 'Feedback' AND ts2 < DATE_SUB(NOW(), INTERVAL {$after}) ")->execute([]); - while ($bug = $res->fetchRow(PDO::FETCH_ASSOC)) + while ($bug = $res->fetch()) { list($mailto, $mailfrom, $bcc, $params) = get_package_mail($bug['package_name'], false, $bug['bug_type']); diff --git a/src/Database/Database.php b/src/Database/Database.php deleted file mode 100644 index 467f1895..00000000 --- a/src/Database/Database.php +++ /dev/null @@ -1,33 +0,0 @@ - - */ -class Database extends \PDO -{ - /** - * When creating new PDO object, automagically switch PDOStatement with own - * extended implementation. - */ - public function __construct(string $dsn, string $username = '', string $password = '', array $options = []) - { - parent::__construct($dsn, $username, $password, $options); - - $this->setAttribute(\PDO::ATTR_STATEMENT_CLASS, [Statement::class]); - } - - /** - * PDO puts apostrophes around the text so we need to strip the outermost - * characters. - */ - public function escape($text, $escape_wildcards = false) - { - return substr($this->quote($text), 1, -1); - } -} diff --git a/src/Database/Statement.php b/src/Database/Statement.php index 75b545b3..f967ef9f 100644 --- a/src/Database/Statement.php +++ b/src/Database/Statement.php @@ -13,34 +13,10 @@ class Statement extends \PDOStatement * \PDOStatement::execute(), on the other hand, returns boolean. Change it * to return $this and thus allow further method chaining. */ - public function execute($input_parameters = null) + public function execute($parameters = null): self { - parent::execute($input_parameters); + parent::execute($parameters); return $this; } - - public function fetchAll($fetchode = null, $rekey = false, $force_array = false, $group = false) - { - return parent::fetchAll(); - } - - public function fetchCol($colnum) - { - return parent::fetchColumn($colnum); - } - - public function fetchOne($colnum = 0, $rownum = null) - { - return $this->fetch(\PDO::FETCH_NUM)[0]; - } - - public function fetchRow($mode = null) - { - if (!$mode) { - $mode = \PDO::FETCH_BOTH; - } - - return $this->fetch($mode); - } } diff --git a/src/Repository/ObsoletePatchRepository.php b/src/Repository/ObsoletePatchRepository.php index 2335ec9e..2682cee0 100644 --- a/src/Repository/ObsoletePatchRepository.php +++ b/src/Repository/ObsoletePatchRepository.php @@ -2,8 +2,6 @@ namespace App\Repository; -use App\Database\Database; - /** * Repository for retrieving data from the bugdb_obsoletes_patches database table. */ @@ -11,14 +9,14 @@ class ObsoletePatchRepository { /** * Database handler. - * @var Database + * @var \PDO */ private $dbh; /** * Class constructor. */ - public function __construct(Database $dbh) + public function __construct(\PDO $dbh) { $this->dbh = $dbh; } diff --git a/src/Repository/PackageRepository.php b/src/Repository/PackageRepository.php index 178a485a..96392c89 100644 --- a/src/Repository/PackageRepository.php +++ b/src/Repository/PackageRepository.php @@ -2,8 +2,6 @@ namespace App\Repository; -use App\Database\Database; - /** * Repository class for retrieving data from the bugdb_pseudo_packages database * table. @@ -12,7 +10,7 @@ class PackageRepository { /** * Database handler. - * @var Database + * @var \PDO */ private $dbh; @@ -27,7 +25,7 @@ class PackageRepository /** * Class constructor. */ - public function __construct(Database $dbh) + public function __construct(\PDO $dbh) { $this->dbh = $dbh; } diff --git a/src/Repository/PatchRepository.php b/src/Repository/PatchRepository.php index a3a6d87e..971c1ffc 100644 --- a/src/Repository/PatchRepository.php +++ b/src/Repository/PatchRepository.php @@ -2,8 +2,6 @@ namespace App\Repository; -use App\Database\Database; - /** * Repository for retrieving data from the bugdb_patchtracker database table. */ @@ -11,7 +9,7 @@ class PatchRepository { /** * Database handler. - * @var Database + * @var \PDO */ private $dbh; @@ -24,7 +22,7 @@ class PatchRepository /** * Class constructor. */ - public function __construct(Database $dbh) + public function __construct(\PDO $dbh) { $this->dbh = $dbh; $this->uploadsDir = BUG_PATCHTRACKER_TMPDIR; @@ -56,7 +54,7 @@ public function findDeveloper(int $bugId, string $patch, int $revision): string $arguments = [$bugId, $patch, $revision]; - return $this->dbh->prepare($sql)->execute($arguments)->fetchOne(); + return $this->dbh->prepare($sql)->execute($arguments)->fetch(\PDO::FETCH_NUM)[0]; } /** @@ -83,7 +81,7 @@ public function getPatchContents(int $bugId, string $name, int $revision): strin WHERE bugdb_id = ? AND patch = ? AND revision = ? '; - if ($this->dbh->prepare($sql)->execute([$bugId, $name, $revision])->fetchOne()) { + if ($this->dbh->prepare($sql)->execute([$bugId, $name, $revision])->fetch(\PDO::FETCH_NUM)[0]) { $contents = @file_get_contents($this->getPatchPath($bugId, $name, $revision)); if (!$contents) { diff --git a/src/Repository/PullRequestRepository.php b/src/Repository/PullRequestRepository.php index 64ab66a8..89a72cee 100644 --- a/src/Repository/PullRequestRepository.php +++ b/src/Repository/PullRequestRepository.php @@ -9,13 +9,14 @@ class PullRequestRepository { /** * Database handler. + * @var \PDO */ private $dbh; /** * Class constructor. */ - public function __construct($dbh) + public function __construct(\PDO $dbh) { $this->dbh = $dbh; } diff --git a/src/Utils/GitHub.php b/src/Utils/GitHub.php index c551959c..b31421e5 100644 --- a/src/Utils/GitHub.php +++ b/src/Utils/GitHub.php @@ -9,6 +9,7 @@ class GitHub { /** * Database handler. + * @var \PDO */ private $dbh; @@ -30,7 +31,7 @@ class GitHub /** * Class constructor */ - public function __construct($dbh) + public function __construct(\PDO $dbh) { $this->dbh = $dbh; } diff --git a/src/Utils/PatchTracker.php b/src/Utils/PatchTracker.php index 127831a1..028f9f1c 100644 --- a/src/Utils/PatchTracker.php +++ b/src/Utils/PatchTracker.php @@ -3,7 +3,6 @@ namespace App\Utils; use App\Utils\Uploader; -use App\Database\Database; /** * Service for handling uploaded patches. @@ -12,7 +11,7 @@ class PatchTracker { /** * Database handler. - * @var Database + * @var \PDO */ private $dbh; @@ -49,7 +48,7 @@ class PatchTracker /** * Class constructor. */ - public function __construct(Database $dbh, Uploader $uploader) + public function __construct(\PDO $dbh, Uploader $uploader) { $this->dbh = $dbh; $this->uploadsDir = BUG_PATCHTRACKER_TMPDIR; diff --git a/www/admin/index.php b/www/admin/index.php index f7952a59..ebb905b6 100644 --- a/www/admin/index.php +++ b/www/admin/index.php @@ -59,7 +59,7 @@ "); echo "

      \n"; - while ($row = $res->fetchRow(PDO::FETCH_ASSOC)) { + while ($row = $res->fetch()) { echo "
      ", $row['name'], ":
      \n
      ", mailto_list(explode(',', $row['list_email'])), "
      \n"; } echo "
      \n"; @@ -73,7 +73,7 @@ echo "

      List Responses

      \n"; $rows = []; - while ($row = $res->fetchRow(PDO::FETCH_ASSOC)) { + while ($row = $res->fetch()) { // This is ugly but works (tm) $row['message'] = nl2br($row['message']); @@ -86,13 +86,13 @@ $sql = "SELECT version() mysql_version\n"; - while ($row = $res->fetchRow(PDO::FETCH_NUM)) { + while ($row = $res->fetch(\PDO::FETCH_NUM)) { $table = $row[0]; $sql .= "\t, (SELECT COUNT(*) FROM `$table`) `cnt_$table`\n"; } $res = $dbh->query($sql); - $row = $res->fetchRow(PDO::FETCH_ASSOC); + $row = $res->fetch(); echo "

      Running MySQL ".$row['mysql_version']."

      "; unset($row['mysql_version']); @@ -110,7 +110,7 @@ $rows = []; $res = $dbh->query("SHOW TABLE STATUS"); echo "

      Table status:

      \n"; - while ($row = $res->fetchRow(PDO::FETCH_ASSOC)) { + while ($row = $res->fetch()) { $rows[] = $row; } diff --git a/www/api.php b/www/api.php index 4c664ee8..4a07baf2 100644 --- a/www/api.php +++ b/www/api.php @@ -27,7 +27,7 @@ "; //@todo add error handling - $rows = $dbh->prepare($query)->execute([])->fetchAll(PDO::FETCH_ASSOC); + $rows = $dbh->prepare($query)->execute([])->fetchAll(); if (!$rows) { echo 'The fail train has arrived.'; exit; diff --git a/www/bug-pwd-finder.php b/www/bug-pwd-finder.php index ffe8a38c..b124b22f 100644 --- a/www/bug-pwd-finder.php +++ b/www/bug-pwd-finder.php @@ -29,7 +29,7 @@ $query = "SELECT email, passwd FROM bugdb WHERE id = '{$bug_id}'"; // Run the query - $row = $dbh->prepare($query)->execute()->fetchRow(PDO::FETCH_ASSOC); + $row = $dbh->prepare($query)->execute()->fetch(); if (is_null($row)) { $errors[] = "Invalid bug id provided: #{$bug_id}"; diff --git a/www/index.php b/www/index.php index b744d089..40e56acf 100644 --- a/www/index.php +++ b/www/index.php @@ -17,7 +17,7 @@ $query = "SELECT id FROM bugdb WHERE status NOT IN('Closed', 'Not a bug', 'Duplicate', 'Spam', 'Wont fix', 'No Feedback') AND private = 'N' ORDER BY RAND() LIMIT 1"; $result = $dbh->prepare($query)->execute(); - $id = $result->fetchRow(); + $id = $result->fetch(\PDO::FETCH_NUM); redirect("bug.php?id={$id[0]}"); } diff --git a/www/lstats.php b/www/lstats.php index ff8cc205..2a22c4c7 100644 --- a/www/lstats.php +++ b/www/lstats.php @@ -23,14 +23,14 @@ function get_status_count ($status, $category = '') $excluded = "'Feature/Change Request', 'Systems problem', 'Website Problem', 'PEAR related', 'PECL related', 'Documentation problem', 'Translation problem', 'PHP-GTK related', 'Online Doc Editor problem'"; if ($category != '') { - $query.= " {$status} AND bug_type = 'Bug' AND package_name = '" . $dbh->escape($category). "' "; + $query.= " {$status} AND bug_type = 'Bug' AND package_name = " . $dbh->quote($category); } else { $query.= " status='{$status}' "; } $query.= "AND bug_type NOT IN({$excluded})"; $res = $dbh->prepare($query)->execute([]); - $row = $res->fetchRow(PDO::FETCH_NUM); + $row = $res->fetch(\PDO::FETCH_NUM); return $row[0]; } diff --git a/www/report.php b/www/report.php index 017cea1a..7009edbb 100644 --- a/www/report.php +++ b/www/report.php @@ -122,7 +122,7 @@ WHERE bug = ? ORDER BY id DESC LIMIT 1 - ")->execute([$row['id']])->fetchOne(); + ")->execute([$row['id']])->fetch(\PDO::FETCH_NUM)[0]; $summary = $row['ldesc']; if (strlen($summary) > 256) { diff --git a/www/stats.php b/www/stats.php index 8b161c4d..1afc8109 100644 --- a/www/stats.php +++ b/www/stats.php @@ -58,7 +58,7 @@ $result = $dbh->prepare($query)->execute(); -while ($row = $result->fetchRow(PDO::FETCH_ASSOC)) { +while ($row = $result->fetch()) { $pkg_tmp[$row['status']][$row['package_name']] = $row['quant']; @$pkg_total[$row['package_name']] += $row['quant']; @$all[$row['status']] += $row['quant']; @@ -160,7 +160,7 @@ $result = $dbh->prepare($query)->execute(); $last_date = null; -while ($row = $result->fetchRow(PDO::FETCH_ASSOC)) { +while ($row = $result->fetch()) { if ($row['d'] != $last_date) { if ($last_date !== null) { echo "\n\n"; diff --git a/www/vote.php b/www/vote.php index 94df5fe0..e3e0a87f 100644 --- a/www/vote.php +++ b/www/vote.php @@ -20,7 +20,7 @@ $samever = isset($_POST['samever']) ? (int) $_POST['samever'] : 0; $sameos = isset($_POST['sameos']) ? (int) $_POST['sameos'] : 0; -if (!$dbh->prepare("SELECT id FROM bugdb WHERE id= ? LIMIT 1")->execute([$id])->fetchOne()) { +if (!$dbh->prepare("SELECT id FROM bugdb WHERE id= ? LIMIT 1")->execute([$id])->fetch(\PDO::FETCH_NUM)[0]) { session_start(); // Authenticate @@ -63,7 +63,7 @@ function get_real_ip () // Check whether the user has already voted on this bug. $bug_check = $dbh->prepare("SELECT bug, ip FROM bugdb_votes WHERE bug = ? AND ip = ? LIMIT 1") ->execute([$id, $ip]) - ->fetchRow(); + ->fetch(\PDO::FETCH_BOTH); if (empty($bug_check)) { // If the user vote isn't found, create one. From 7dd4451cd5249c45c46e15a1dded596656ed9a2c Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 16 Dec 2018 23:20:48 +0100 Subject: [PATCH 157/277] Remove unused styles and web assets Some of these styles and web assets were used in the past when bug trackings for PECL and PHP were separated. Today PECL bugs are located in the bugs.php.net also. This patch removes outdated styles and images. --- www/css/style.css | 152 --------------------------------------- www/images/box-0.gif | Bin 54 -> 0 bytes www/images/box-1.gif | Bin 53 -> 0 bytes www/images/item.gif | Bin 506 -> 0 bytes www/images/pecl_item.gif | Bin 362 -> 0 bytes www/images/php_item.gif | Bin 506 -> 0 bytes www/images/spacer.gif | Bin 43 -> 0 bytes 7 files changed, 152 deletions(-) delete mode 100644 www/images/box-0.gif delete mode 100644 www/images/box-1.gif delete mode 100644 www/images/item.gif delete mode 100644 www/images/pecl_item.gif delete mode 100644 www/images/php_item.gif delete mode 100644 www/images/spacer.gif diff --git a/www/css/style.css b/www/css/style.css index 2efd09d8..3f6c000f 100644 --- a/www/css/style.css +++ b/www/css/style.css @@ -80,44 +80,6 @@ td.head-menu a:visited { text-decoration: none; } -/* ========= Sidebars ========= */ - -td.sidebar_left { - width: 149px; - background-color: #F0F0F0; - vertical-align: top; - font-size: 90%; - border-right: 1px dashed #ccc; - padding: 1em 0.3em 0.3em 0.3em; -} - -td.sidebar_right { - width: 149px; - background-color: #F0F0F0; - vertical-align: top; - font-size: 90%; - border-left: 1px dashed #ccc; - padding: 1em 0.3em 0.3em 0.3em; -} - -table.sidebar-releases { - font-size: 100%; /* make IE look normal */ -} - -ul.side_pages { - margin-top: 0.1em; - margin-bottom: 1.5em; - margin-left: 1em; - padding-left: 1em; - list-style: url(http://github.com/images/box-0.gif) square; -} - -li.side_page { - text-indent: -0.4em; - white-space: nowrap; -} - - /* ========= Footer ========= */ td.foot-bar { @@ -153,16 +115,6 @@ table.form-holder { width: auto; } -caption.form-caption { - font-weight: bold; - color: #FFFFFF; - background-color: #5b69a6; - padding: 0.3em; - border-left: 1px solid #FFFFFF; - border-right: 1px solid #FFFFFF; - margin-top: 0.5em; -} - /* Labels on the left side of form and table rows */ th.form-label_left { padding: 3px; @@ -174,16 +126,6 @@ th.form-label_left { width: 25%; } -/* Labels on the top of form and table columns */ -th.form-label_top, -th.form-label_top_center { - padding: 3px; - text-align: center; - font-weight: bold; - color: #FFFFFF; - background-color: #5b69a6; -} - td.form-input { padding: 3px; text-align: left; @@ -203,10 +145,6 @@ td.form-input_center input { font-size: 90%; } -input.makeDocBug { - background-color: #8CDD81; - border: 3px ridge green; -} p.cell_note { margin-top: 0.3em; margin-bottom: 0.2em; @@ -234,34 +172,6 @@ form { margin-bottom: 0; } -submit { - border: 0px; -} - -/* === Voting === */ - -td.vote-active, td.vote-winner { - padding: 3px; - text-align: left; - vertical-align: top; - background-color: #ffbbaa; -} - -td.vote-complete { - padding: 3px; - text-align: left; - vertical-align: top; - background-color: #bbaaff; -} - -td.vote-inactive { - padding: 3px; - text-align: left; - vertical-align: top; - background-color: #e8e8e8; -} - - /* === User Submission Responses === */ div.errors, @@ -377,27 +287,11 @@ a:hover { color: #000033; } -th.headrow { - text-align: left; - border-bottom: 1px solid #000; - white-space: nowrap; -} - th.others { color: #006600; text-align: left; } -td.textcell { - vertical-align: top; - padding-left: 1.2em; - padding-bottom: 1em; -} - -td.textcell p { - margin-top: 0em; -} - td.ulcell { vertical-align: top; } @@ -413,12 +307,6 @@ hr { background-color: #ccc; } -.newsDate { - font-size: 85%; - font-style: italic; - color: #66cc66; -} - code, pre { font-family: Consolas, Inconsolata, "Liberation Mono", monospace; font-size: 13px; @@ -463,40 +351,10 @@ a.small { text-decoration: none; } -.tableTitle { - font-weight: bold; -} - -.tableExtras { - font-size: 85%; - color: #FFFFFF; -} - -.tip { - border: 1px solid #00c; - color: #006; - background: #eef; - padding: 8px; -} - -ul.spaced li { - margin-bottom: 0.75em; -} - .indent { margin-left: 20px; } -hr.greyline { - border-top: thin solid #CCCCCC; - margin-top: 0px; - margin-bottom: 0px; -} - -.underline { - text-decoration: underline; -} - .accesskey { text-decoration: underline; } @@ -519,12 +377,6 @@ td.content table { padding-bottom: 15px; } -.headertop { - background: #5b69a6; -} -.headersep { - background: #000033; -} .headerbottom { background: #9999cc; } @@ -706,10 +558,6 @@ table#votes { } /* === Search === */ -a.status_active { - font-weight: bold; -} - td.search-prev_next { background-color: #cccccc; text-align: center; diff --git a/www/images/box-0.gif b/www/images/box-0.gif deleted file mode 100644 index 65d30d7df1aa3c2a84279797badca3fa9c5d7dcf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54 zcmZ?wbhEHb+g(=nz^~icYC{qhp&;7xp8*0c6zpWe7ALZ zvwMHLcYCyRcdTn}n}vzMb$5|PMp0Z{YI}a2YHWWsHaWn+zybmQ2?+)S1OpQj4-O3q z9v&VB1_uBC{{R30A^8LW000&MEC2ui03ZM$06+!*pe2rEX`ZO%1;H>F3&T8*EGvyO z8ADJ`wg&|efk-qPK_)_r00bKjgi--WYK%oB#qD~(semCU!DuuXPGM*^Qzn}Ya&t{6 zB0fu?u%5(hfHDgc6a@-`3ltFpV2zHCkdb-=4+;ka377~725AqNGMNOU2o4l^1vZ=- z9G{&W6&NrORuTXc1P@LS1X*}M2oF^gd=CqJ1qjCmLlOoB23QLT6?r2q1r7~|3<=)f z3^)!F6av{C9`hqF}Q}Q61_x1jT$=xU+Lu zeT4+tQkmI(IJkm}+g;eD7=!9_gn6BmSh)&|av1xZIe3C9b3$DzX6JBMyA*OQWfTbW anYEOyzd1IQi*;V2kg$Lt|A9nD25SI6l33&b diff --git a/www/images/php_item.gif b/www/images/php_item.gif deleted file mode 100644 index a7f3243cd3cf7e83133ecbfbbcf123ecbaa55a57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 506 zcmV+g(=nz^~icYC{qhp&;7xp8*0c6zpWe7ALZ zvwMHLcYCyRcdTn}n}vzMb$5|PMp0Z{YI}a2YHWWsHaWn+zybmQ2?+)S1OpQj4-O3q z9v&VB1_uBC{{R30A^8LW000&MEC2ui03ZM$06+!*pe2rEX`ZO%1;H>F3&T8*EGvyO z8ADJ`wg&|efk-qPK_)_r00bKjgi--WYK%oB#qD~(semCU!DuuXPGM*^Qzn}Ya&t{6 zB0fu?u%5(hfHDgc6a@-`3ltFpV2zHCkdb-=4+;ka377~725AqNGMNOU2o4l^1vZ=- z9G{&W6&NrORuTXc1P@LS1X*}M2oF^gd=CqJ1qjCmLlOoB23QLT6?r2q1r7~|3<=)f z3^)!F6av Date: Sun, 16 Dec 2018 10:07:30 +0100 Subject: [PATCH 158/277] Move fetching bug comments to a repository class Changes: - This moves fetching bug comments to a dedicated repository class - It uses vanilla PDO as current direction of the database usage is applied in this app. - When bug_id is set to preview types issues occur due to int type hint. Should be refactored more in the future commits. --- include/functions.php | 20 ------------- src/Repository/CommentRepository.php | 42 ++++++++++++++++++++++++++++ www/bug.php | 5 +++- www/rss/bug.php | 5 +++- 4 files changed, 50 insertions(+), 22 deletions(-) create mode 100644 src/Repository/CommentRepository.php diff --git a/include/functions.php b/include/functions.php index 6206db3a..e3d9a971 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1482,26 +1482,6 @@ function bugs_get_bug($bug_id) return $dbh->prepare($query)->execute([$bug_id])->fetch(); } -/** - * Fetch bug comments - * - * @return mixed array of bug comments or object with error info - */ -function bugs_get_bug_comments($bug_id) -{ - global $dbh; - - $query = " - SELECT c.id, c.email, c.comment, c.comment_type, - UNIX_TIMESTAMP(c.ts) AS added, - c.reporter_name AS comment_name - FROM bugdb_comments c - WHERE c.bug = ? - GROUP BY c.id ORDER BY c.ts - "; - return $dbh->prepare($query)->execute([$bug_id])->fetchAll(); -} - /** * Add bug comment */ diff --git a/src/Repository/CommentRepository.php b/src/Repository/CommentRepository.php new file mode 100644 index 00000000..8f28c4b8 --- /dev/null +++ b/src/Repository/CommentRepository.php @@ -0,0 +1,42 @@ +dbh = $dbh; + } + + /** + * Fetch bug comments + */ + public function findByBugId(int $id): array + { + $sql = 'SELECT c.id, c.email, c.comment, c.comment_type, + UNIX_TIMESTAMP(c.ts) AS added, + c.reporter_name AS comment_name + FROM bugdb_comments c + WHERE c.bug = ? + GROUP BY c.id ORDER BY c.ts + '; + + $statement = $this->dbh->prepare($sql); + $statement->execute([$id]); + + return $statement->fetchAll(); + } +} diff --git a/www/bug.php b/www/bug.php index 502e50a0..29c10f1c 100644 --- a/www/bug.php +++ b/www/bug.php @@ -1,6 +1,7 @@ findByBugId($bug_id) : []; + if ($show_bug_info && is_array($bug_comments) && count($bug_comments) && $bug['status'] !== 'Spam') { $history_tabs = [ 'type_all' => 'All', diff --git a/www/rss/bug.php b/www/rss/bug.php index 32684046..05468202 100644 --- a/www/rss/bug.php +++ b/www/rss/bug.php @@ -1,5 +1,7 @@ findByBugId($bug_id); if ($format == 'xml') { header('Content-type: text/xml; charset=utf-8'); From a5b6fa0704230cfb13b1fe592ec0908924ccb1a8 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 16 Dec 2018 18:08:33 +0100 Subject: [PATCH 159/277] Refactor get_resolve_reasons() to repository class --- include/functions.php | 32 ----------------- scripts/cron/no-feedback | 6 +++- src/Repository/ReasonRepository.php | 55 +++++++++++++++++++++++++++++ www/bug.php | 4 ++- www/fix.php | 5 ++- www/quick-fix-desc.php | 5 ++- www/report.php | 4 ++- 7 files changed, 74 insertions(+), 37 deletions(-) create mode 100644 src/Repository/ReasonRepository.php diff --git a/include/functions.php b/include/functions.php index e3d9a971..5384fb4f 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1422,38 +1422,6 @@ function unsubscribe($bug_id, $hash) return true; } - -/** - * Fetch bug resolves - * - * @return array array of resolves - */ -function get_resolve_reasons($project = false) -{ - global $dbh; - - $where = ''; - - if ($project !== false) { - $project = $dbh->quote($project); - $where.= "WHERE (project = {$project} OR project = '')"; - } - - $resolves = $variations = []; - $res = $dbh->prepare("SELECT * FROM bugdb_resolves $where")->execute([]); - if (!$res) { - throw new Exception("SQL Error in get_resolve_reasons"); - } - while ($row = $res->fetch()) { - if (!empty($row['package_name'])) { - $variations[$row['name']][$row['package_name']] = $row['message']; - } else { - $resolves[$row['name']] = $row; - } - } - return [$resolves, $variations]; -} - /** * Fetch bug data * diff --git a/scripts/cron/no-feedback b/scripts/cron/no-feedback index e3154f8d..25f15b53 100755 --- a/scripts/cron/no-feedback +++ b/scripts/cron/no-feedback @@ -3,6 +3,8 @@ # this script closes bugs due to lack of feedback. +use App\Repository\ReasonRepository; + require __DIR__.'/../../include/prepend.php'; # date interval to close after @@ -14,7 +16,9 @@ $in = ['status' => 'No Feedback']; # Update relevant reports if ($dbh) { - list($RESOLVE_REASONS, $FIX_VARIATIONS) = get_resolve_reasons($site); + $reasonRepository = new ReasonRepository($dbh); + + list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($site); $res = $dbh->prepare(" SELECT id, package_name, bug_type, email, passwd, sdesc, ldesc, php_version, diff --git a/src/Repository/ReasonRepository.php b/src/Repository/ReasonRepository.php new file mode 100644 index 00000000..c8d74c89 --- /dev/null +++ b/src/Repository/ReasonRepository.php @@ -0,0 +1,55 @@ +dbh = $dbh; + } + + /** + * Fetch bug resolves. + */ + public function findByProject(string $project = ''): array + { + $sql = 'SELECT * FROM bugdb_resolves'; + $arguments = []; + + if ($project !== '') { + $sql = " WHERE (project = ? OR project = '')"; + $arguments[] = $project; + } + + $resolves = $variations = []; + $statement = $this->dbh->prepare($sql); + $exec = $statement->execute($arguments); + + if (!$exec) { + throw new \Exception('Error when fetching resolve reasons.'); + } + + while ($row = $statement->fetch()) { + if (!empty($row['package_name'])) { + $variations[$row['name']][$row['package_name']] = $row['message']; + } else { + $resolves[$row['name']] = $row; + } + } + + return [$resolves, $variations]; + } +} diff --git a/www/bug.php b/www/bug.php index 29c10f1c..a41065a3 100644 --- a/www/bug.php +++ b/www/bug.php @@ -7,6 +7,7 @@ use App\Repository\PatchRepository; use App\Utils\Captcha; use App\Repository\PullRequestRepository; +use App\Repository\ReasonRepository; // Obtain common includes require_once '../include/prepend.php'; @@ -190,7 +191,8 @@ // Fetch RESOLVE_REASONS array if ($edit === 1) { - list($RESOLVE_REASONS, $FIX_VARIATIONS) = get_resolve_reasons($project); + $reasonRepository = new ReasonRepository($dbh); + list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($project); } if (isset($_POST['ncomment']) && !isset($_POST['preview']) && $edit == 3) { diff --git a/www/fix.php b/www/fix.php index 963cb435..9ec71580 100644 --- a/www/fix.php +++ b/www/fix.php @@ -1,5 +1,7 @@ findByProject($site); // Handle reason / comments $reason = filter_var($_REQUEST['r'], FILTER_SANITIZE_STRING); diff --git a/www/quick-fix-desc.php b/www/quick-fix-desc.php index 5d6c67b2..01ff0989 100644 --- a/www/quick-fix-desc.php +++ b/www/quick-fix-desc.php @@ -1,11 +1,14 @@ findByProject($site); // Authenticate bugs_authenticate($user, $pw, $logged_in, $user_flags); diff --git a/www/report.php b/www/report.php index 7009edbb..408dfdf0 100644 --- a/www/report.php +++ b/www/report.php @@ -1,6 +1,7 @@ findByProject($_GET['project'] ?? ''); $dev_extra = ''; $maxkeysize = 0; From 10b0ad9cbe6a764b72b2fad0ed2ce3d349992d6c Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 16 Dec 2018 20:45:17 +0100 Subject: [PATCH 160/277] Refactor bugs_get_bug() to repository --- include/functions.php | 28 ------------------ src/Repository/BugRepository.php | 51 ++++++++++++++++++++++++++++++++ www/bug.php | 4 ++- www/fix.php | 4 ++- www/gh-pull-add.php | 5 +++- www/patch-add.php | 5 +++- www/patch-display.php | 5 +++- www/rpc.php | 5 +++- www/rss/bug.php | 4 ++- 9 files changed, 76 insertions(+), 35 deletions(-) create mode 100644 src/Repository/BugRepository.php diff --git a/include/functions.php b/include/functions.php index 5384fb4f..767a9298 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1422,34 +1422,6 @@ function unsubscribe($bug_id, $hash) return true; } -/** - * Fetch bug data - * - * @return mixed array of bug data or object with error info - */ -function bugs_get_bug($bug_id) -{ - global $dbh; - - $query = 'SELECT b.id, b.package_name, b.bug_type, b.email, b.reporter_name, - b.sdesc, b.ldesc, b.php_version, b.php_os, - b.status, b.ts1, b.ts2, b.assign, b.block_user_comment, - b.private, b.cve_id, - UNIX_TIMESTAMP(b.ts1) AS submitted, - UNIX_TIMESTAMP(b.ts2) AS modified, - COUNT(bug=b.id) AS votes, - IFNULL((SELECT z.project FROM bugdb_pseudo_packages z WHERE z.name = b.package_name LIMIT 1), "php") project, - SUM(reproduced) AS reproduced, SUM(tried) AS tried, - SUM(sameos) AS sameos, SUM(samever) AS samever, - AVG(score)+3 AS average, STD(score) AS deviation - FROM bugdb b - LEFT JOIN bugdb_votes ON b.id = bug - WHERE b.id = ? - GROUP BY bug'; - - return $dbh->prepare($query)->execute([$bug_id])->fetch(); -} - /** * Add bug comment */ diff --git a/src/Repository/BugRepository.php b/src/Repository/BugRepository.php new file mode 100644 index 00000000..66ac5113 --- /dev/null +++ b/src/Repository/BugRepository.php @@ -0,0 +1,51 @@ +dbh = $dbh; + } + + /** + * Fetch bug data by bug id. + */ + public function findOneById(int $id): array + { + $sql = 'SELECT b.id, b.package_name, b.bug_type, b.email, b.reporter_name, + b.sdesc, b.ldesc, b.php_version, b.php_os, + b.status, b.ts1, b.ts2, b.assign, b.block_user_comment, + b.private, b.cve_id, + UNIX_TIMESTAMP(b.ts1) AS submitted, + UNIX_TIMESTAMP(b.ts2) AS modified, + COUNT(bug=b.id) AS votes, + IFNULL((SELECT z.project FROM bugdb_pseudo_packages z WHERE z.name = b.package_name LIMIT 1), "php") project, + SUM(reproduced) AS reproduced, SUM(tried) AS tried, + SUM(sameos) AS sameos, SUM(samever) AS samever, + AVG(score)+3 AS average, STD(score) AS deviation + FROM bugdb b + LEFT JOIN bugdb_votes ON b.id = bug + WHERE b.id = ? + GROUP BY bug + '; + + $statement = $this->dbh->prepare($sql); + $statement->execute([$id]); + + return $statement->fetch(); + } +} diff --git a/www/bug.php b/www/bug.php index a41065a3..3d50389a 100644 --- a/www/bug.php +++ b/www/bug.php @@ -1,6 +1,7 @@ findOneById($bug_id); } // DB error diff --git a/www/fix.php b/www/fix.php index 9ec71580..f1be1dbe 100644 --- a/www/fix.php +++ b/www/fix.php @@ -1,5 +1,6 @@ findOneById($bug_id); if (!is_array($bug)) { response_header('No Such Bug'); diff --git a/www/gh-pull-add.php b/www/gh-pull-add.php index c86d4bd4..1821d9ea 100644 --- a/www/gh-pull-add.php +++ b/www/gh-pull-add.php @@ -1,5 +1,6 @@ findOneById($bug_id))) { response_header('Error :: invalid bug selected'); display_bug_error("Invalid bug #{$bug_id} selected"); response_footer(); diff --git a/www/patch-add.php b/www/patch-add.php index 763a011f..f0eaf26e 100644 --- a/www/patch-add.php +++ b/www/patch-add.php @@ -1,5 +1,6 @@ findOneById($bug_id))) { response_header('Error :: invalid bug selected'); display_bug_error("Invalid bug #{$bug_id} selected"); response_footer(); diff --git a/www/patch-display.php b/www/patch-display.php index 1735b1c9..863c3861 100644 --- a/www/patch-display.php +++ b/www/patch-display.php @@ -1,5 +1,6 @@ findOneById($bug_id))) { response_header('Error :: invalid bug selected'); display_bug_error("Invalid bug #{$bug_id} selected"); response_footer(); diff --git a/www/rpc.php b/www/rpc.php index 82976955..1430c7a8 100644 --- a/www/rpc.php +++ b/www/rpc.php @@ -1,5 +1,7 @@ findOneById($bug_id); if (!is_array($bug)) { echo json_encode(['result' => ['error' => 'No such bug']]); diff --git a/www/rss/bug.php b/www/rss/bug.php index 05468202..2198161a 100644 --- a/www/rss/bug.php +++ b/www/rss/bug.php @@ -1,5 +1,6 @@ findOneById($bug_id); if (!$bug) { header('HTTP/1.0 404 Not Found'); From 7203dec380656b953170f5aa63f4d271c2801f65 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 18 Dec 2018 00:28:58 +0100 Subject: [PATCH 161/277] Fix SQL string typo --- src/Repository/ReasonRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Repository/ReasonRepository.php b/src/Repository/ReasonRepository.php index c8d74c89..fd4f411c 100644 --- a/src/Repository/ReasonRepository.php +++ b/src/Repository/ReasonRepository.php @@ -30,7 +30,7 @@ public function findByProject(string $project = ''): array $arguments = []; if ($project !== '') { - $sql = " WHERE (project = ? OR project = '')"; + $sql .= " WHERE (project = ? OR project = '')"; $arguments[] = $project; } From f038854680d7bd625713757555bead86b789e1fe Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 18 Dec 2018 19:55:20 +0100 Subject: [PATCH 162/277] Remove unneeded check for database handle presence This check(s) were once used in previous code for local testings. --- scripts/cron/no-feedback | 73 +++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/scripts/cron/no-feedback b/scripts/cron/no-feedback index 25f15b53..55fb7d6d 100755 --- a/scripts/cron/no-feedback +++ b/scripts/cron/no-feedback @@ -14,44 +14,41 @@ $after = "7 DAY"; $in = ['status' => 'No Feedback']; # Update relevant reports -if ($dbh) +$reasonRepository = new ReasonRepository($dbh); + +list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($site); + +$res = $dbh->prepare(" + SELECT id, package_name, bug_type, email, passwd, sdesc, ldesc, php_version, + php_os, status, ts1, ts2, assign, UNIX_TIMESTAMP(ts1) AS submitted, + private, reporter_name, UNIX_TIMESTAMP(ts2) AS modified + FROM bugdb + WHERE status = 'Feedback' AND ts2 < DATE_SUB(NOW(), INTERVAL {$after}) +")->execute([]); + +while ($bug = $res->fetch()) { - $reasonRepository = new ReasonRepository($dbh); - - list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($site); - - $res = $dbh->prepare(" - SELECT id, package_name, bug_type, email, passwd, sdesc, ldesc, php_version, - php_os, status, ts1, ts2, assign, UNIX_TIMESTAMP(ts1) AS submitted, - private, reporter_name, UNIX_TIMESTAMP(ts2) AS modified - FROM bugdb - WHERE status = 'Feedback' AND ts2 < DATE_SUB(NOW(), INTERVAL {$after}) - ")->execute([]); - - while ($bug = $res->fetch()) - { - list($mailto, $mailfrom, $bcc, $params) = get_package_mail($bug['package_name'], false, $bug['bug_type']); - - // No feedback message - if (isset($FIX_VARIATIONS) && isset($FIX_VARIATIONS['nofeedback'][$bug['package_name']])) { - $message = $FIX_VARIATIONS['nofeedback'][$bug['package_name']]; - } elseif (isset($RESOLVE_REASONS['nofeedback'])) { - $message = $RESOLVE_REASONS['nofeedback']['message']; - } else { - die('[no-feedback] Could not find resolve reason! (this should not happen!)'); - } - bugs_add_comment($bug['id'], $mailfrom, '', $message, 'comment'); - - // Update status - $dbh->prepare(' - UPDATE bugdb - SET status = "No Feedback", ts2 = NOW() - WHERE id = ? - ')->execute([ - $bug['id'], - ]); - - // Send emails - mail_bug_updates($bug, $in, $mailfrom, $message); + list($mailto, $mailfrom, $bcc, $params) = get_package_mail($bug['package_name'], false, $bug['bug_type']); + + // No feedback message + if (isset($FIX_VARIATIONS) && isset($FIX_VARIATIONS['nofeedback'][$bug['package_name']])) { + $message = $FIX_VARIATIONS['nofeedback'][$bug['package_name']]; + } elseif (isset($RESOLVE_REASONS['nofeedback'])) { + $message = $RESOLVE_REASONS['nofeedback']['message']; + } else { + die('[no-feedback] Could not find resolve reason! (this should not happen!)'); } + bugs_add_comment($bug['id'], $mailfrom, '', $message, 'comment'); + + // Update status + $dbh->prepare(' + UPDATE bugdb + SET status = "No Feedback", ts2 = NOW() + WHERE id = ? + ')->execute([ + $bug['id'], + ]); + + // Send emails + mail_bug_updates($bug, $in, $mailfrom, $message); } From 11fd2afda1fdbb0e0365886cd9cafc7b74dd525f Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 19 Dec 2018 02:22:12 +0100 Subject: [PATCH 163/277] Move lists query to repository class --- src/Repository/PackageRepository.php | 16 ++++++++++++++++ www/admin/index.php | 14 ++++---------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/Repository/PackageRepository.php b/src/Repository/PackageRepository.php index 96392c89..aaaa399c 100644 --- a/src/Repository/PackageRepository.php +++ b/src/Repository/PackageRepository.php @@ -117,4 +117,20 @@ private function getNested(array $data): array return $packages; } + + /** + * Find all package mailing lists. + */ + public function findLists(): array + { + $sql = "SELECT name, list_email + FROM bugdb_pseudo_packages + WHERE project = 'php' AND LENGTH(list_email) > 0 + ORDER BY list_email + "; + + $statement = $this->dbh->query($sql); + + return $statement->fetchAll(); + } } diff --git a/www/admin/index.php b/www/admin/index.php index ebb905b6..1c2c810d 100644 --- a/www/admin/index.php +++ b/www/admin/index.php @@ -1,4 +1,7 @@ query(" - SELECT name, list_email - FROM bugdb_pseudo_packages - WHERE project = 'php' - AND LENGTH(list_email) > 0 - ORDER BY list_email - "); - echo "
      \n"; - while ($row = $res->fetch()) { + foreach ((new PackageRepository($dbh))->findLists() as $row) { echo "
      ", $row['name'], ":
      \n
      ", mailto_list(explode(',', $row['list_email'])), "
      \n"; } echo "
      \n"; From ad8bc2d5ab343733e57622d0ca063ce96570509d Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 19 Dec 2018 01:47:19 +0100 Subject: [PATCH 164/277] Move bugdb queries to repository --- scripts/cron/email-assigned | 17 +---- scripts/cron/no-feedback | 14 +--- src/Repository/BugRepository.php | 126 +++++++++++++++++++++++++++++++ www/index.php | 7 +- www/stats.php | 36 ++------- www/vote.php | 4 +- 6 files changed, 143 insertions(+), 61 deletions(-) diff --git a/scripts/cron/email-assigned b/scripts/cron/email-assigned index c1977d49..60456a6d 100755 --- a/scripts/cron/email-assigned +++ b/scripts/cron/email-assigned @@ -1,6 +1,8 @@ #!/usr/bin/env php 1 - AND status IN ('Assigned', 'Open', 'Re-Opened', 'Feedback', 'Analyzed', 'Verified', 'Critical', 'Suspended') - ORDER BY id"; - -$res = $dbh->query($sql); - -// Gather up the data -while ($row = $res->fetch()) { - $data[$row['assign']][] = $row; -} - -foreach ($data as $assigned => $binfos) { +foreach ((new BugRepository($dbh))->findAllAssigned() as $assigned => $binfos) { $mbody = format_email_body($binfos); $email_user = $assigned . '@php.net'; diff --git a/scripts/cron/no-feedback b/scripts/cron/no-feedback index 55fb7d6d..653ab8d3 100755 --- a/scripts/cron/no-feedback +++ b/scripts/cron/no-feedback @@ -3,13 +3,11 @@ # this script closes bugs due to lack of feedback. +use App\Repository\BugRepository; use App\Repository\ReasonRepository; require __DIR__.'/../../include/prepend.php'; -# date interval to close after -$after = "7 DAY"; - # Set "input" array $in = ['status' => 'No Feedback']; @@ -18,15 +16,7 @@ $reasonRepository = new ReasonRepository($dbh); list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($site); -$res = $dbh->prepare(" - SELECT id, package_name, bug_type, email, passwd, sdesc, ldesc, php_version, - php_os, status, ts1, ts2, assign, UNIX_TIMESTAMP(ts1) AS submitted, - private, reporter_name, UNIX_TIMESTAMP(ts2) AS modified - FROM bugdb - WHERE status = 'Feedback' AND ts2 < DATE_SUB(NOW(), INTERVAL {$after}) -")->execute([]); - -while ($bug = $res->fetch()) +foreach ((new BugRepository($dbh))->findAllWithoutFeedback() as $bug) { list($mailto, $mailfrom, $bcc, $params) = get_package_mail($bug['package_name'], false, $bug['bug_type']); diff --git a/src/Repository/BugRepository.php b/src/Repository/BugRepository.php index 66ac5113..5ef08f3f 100644 --- a/src/Repository/BugRepository.php +++ b/src/Repository/BugRepository.php @@ -13,6 +13,11 @@ class BugRepository */ private $dbh; + /** + * Days when bugs with no feedback get closed. + */ + private const FEEDBACK_PERIOD = 7; + /** * Class constructor. */ @@ -48,4 +53,125 @@ public function findOneById(int $id): array return $statement->fetch(); } + + /** + * Find random bug to resolve for a contributor. + */ + public function findRandom(): array + { + $sql = "SELECT id + FROM bugdb + WHERE status NOT IN('Closed', 'Not a bug', 'Duplicate', 'Spam', 'Wont fix', 'No Feedback') + AND private = 'N' + ORDER BY RAND() LIMIT 1 + "; + + $statement = $this->dbh->prepare($sql); + $statement->execute(); + + return $statement->fetch(\PDO::FETCH_NUM); + } + + /** + * Find all bugs that have someone assigned to them. + */ + public function findAllAssigned(): array + { + $sql = "SELECT id, package_name, bug_type, sdesc, status, assign, UNIX_TIMESTAMP(ts1) AS ts_opened, UNIX_TIMESTAMP(ts2) AS ts_changed + FROM `bugdb` + WHERE length(assign) > 1 + AND status IN ('Assigned', 'Open', 'Re-Opened', 'Feedback', 'Analyzed', 'Verified', 'Critical', 'Suspended') + ORDER BY id + "; + + $statement = $this->dbh->query($sql); + + $data = []; + + // Populate data with assign field as array key + while ($row = $statement->fetch()) { + $data[$row['assign']][] = $row; + } + + return $data; + } + + /** + * Find all bugs without feedback by given period time. + */ + public function findAllWithoutFeedback(int $feedbackPeriod = self::FEEDBACK_PERIOD): array + { + $sql = "SELECT id, package_name, bug_type, email, passwd, sdesc, ldesc, + php_version, php_os, status, ts1, ts2, assign, + UNIX_TIMESTAMP(ts1) AS submitted, private, reporter_name, + UNIX_TIMESTAMP(ts2) AS modified + FROM bugdb + WHERE status = 'Feedback' AND ts2 < DATE_SUB(NOW(), INTERVAL ? DAY) + "; + + $statement = $this->dbh->prepare($sql); + $statement->execute([$feedbackPeriod]); + + return $statement->fetchAll(); + } + + /** + * Find all bugs by given bug type. + */ + public function findAllByBugType(string $type = 'All'): array + { + $sql = 'SELECT b.package_name, b.status, COUNT(*) AS quant FROM bugdb AS b'; + + $arguments = []; + + if ($type !== 'All') { + $sql .= ' WHERE bug_type = ? '; + $arguments[] = $type; + } + + $sql .= ' GROUP BY b.package_name, b.status ORDER BY b.package_name, b.status'; + + $statement = $this->dbh->prepare($sql); + $statement->execute($arguments); + + return $statement->fetchAll(); + } + + /** + * Find bugs for grouping into PHP versions by given bug type. + */ + public function findPhpVersions(string $type = 'All'): array + { + $sql = "SELECT DATE_FORMAT(ts1, '%Y-%m') as d, + IF(b.php_version LIKE '%Git%', LEFT(b.php_version, LOCATE('Git', b.php_version)+2), b.php_version) AS formatted_version, + COUNT(*) AS quant + FROM bugdb AS b + WHERE ts1 >= CONCAT(YEAR(NOW())-1, '-', MONTH(NOW()), '-01 00:00:00') + "; + + $arguments = []; + + if ($type !== 'All') { + $sql .= ' AND bug_type = ? '; + $arguments[] = $type; + } + + $sql .= ' GROUP BY d, formatted_version ORDER BY d, quant'; + + $statement = $this->dbh->prepare($sql); + $statement->execute($arguments); + + return $statement->fetchAll(); + } + + /** + * Check if bug with given id exists. + */ + public function exists(int $id): bool + { + $statement = $this->dbh->prepare('SELECT 1 FROM bugdb WHERE id = ?'); + $statement->execute([$id]); + + return (bool)$statement->fetchColumn(); + } } diff --git a/www/index.php b/www/index.php index 40e56acf..03e40143 100644 --- a/www/index.php +++ b/www/index.php @@ -4,6 +4,8 @@ /* The bug system home page */ +use App\Repository\BugRepository; + // Obtain common includes require_once '../include/prepend.php'; @@ -14,10 +16,7 @@ } if($_SERVER['REQUEST_URI'] == '/random') { - $query = "SELECT id FROM bugdb WHERE status NOT IN('Closed', 'Not a bug', 'Duplicate', 'Spam', 'Wont fix', 'No Feedback') AND private = 'N' ORDER BY RAND() LIMIT 1"; - - $result = $dbh->prepare($query)->execute(); - $id = $result->fetch(\PDO::FETCH_NUM); + $id = (new BugRepository($dbh))->findRandom(); redirect("bug.php?id={$id[0]}"); } diff --git a/www/stats.php b/www/stats.php index 1afc8109..8cdd57aa 100644 --- a/www/stats.php +++ b/www/stats.php @@ -1,5 +1,7 @@ quote($bug_type); -} - -$query = " - SELECT b.package_name, b.status, COUNT(*) AS quant - FROM bugdb AS b - WHERE 1 = 1 {$where} - GROUP BY b.package_name, b.status - ORDER BY b.package_name, b.status -"; +$bug_type = $_GET['bug_type'] ?? 'All'; +$bugRepository = new BugRepository($dbh); -$result = $dbh->prepare($query)->execute(); - -while ($row = $result->fetch()) { +foreach ($bugRepository->findAllByBugType($bug_type) as $row) { $pkg_tmp[$row['status']][$row['package_name']] = $row['quant']; @$pkg_total[$row['package_name']] += $row['quant']; @$all[$row['status']] += $row['quant']; @@ -148,19 +135,8 @@ echo "\n
      \n

      PHP Versions for recent bug reports:

      "; -$query = " SELECT DATE_FORMAT(ts1, '%Y-%m') as d, - IF(b.php_version LIKE '%Git%', LEFT(b.php_version, LOCATE('Git', b.php_version)+2), b.php_version) AS formatted_version, - COUNT(*) AS quant - FROM bugdb AS b - WHERE ts1 >= CONCAT(YEAR(NOW())-1, '-', MONTH(NOW()), '-01 00:00:00') - {$where} - GROUP BY d, formatted_version - ORDER BY d, quant"; - -$result = $dbh->prepare($query)->execute(); - $last_date = null; -while ($row = $result->fetch()) { +foreach ($bugRepository->findPhpVersions($bug_type) as $row) { if ($row['d'] != $last_date) { if ($last_date !== null) { echo "\n\n"; diff --git a/www/vote.php b/www/vote.php index e3e0a87f..73f2a9ca 100644 --- a/www/vote.php +++ b/www/vote.php @@ -1,5 +1,7 @@ prepare("SELECT id FROM bugdb WHERE id= ? LIMIT 1")->execute([$id])->fetch(\PDO::FETCH_NUM)[0]) { +if (!(new BugRepository($dbh))->exists($id)) { session_start(); // Authenticate From a1e9fe6f6a7750b008dfe92cf8ecd9ab877b3f67 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 19 Dec 2018 02:49:59 +0100 Subject: [PATCH 165/277] Move docs comments to repository --- src/Repository/CommentRepository.php | 23 +++++++++++++++++++++++ www/api.php | 19 +++++-------------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/Repository/CommentRepository.php b/src/Repository/CommentRepository.php index 8f28c4b8..18c69441 100644 --- a/src/Repository/CommentRepository.php +++ b/src/Repository/CommentRepository.php @@ -39,4 +39,27 @@ public function findByBugId(int $id): array return $statement->fetchAll(); } + + /** + * Find all log comments for documentation. + * TODO: Check if this method is still used in the api.php endpoint. + */ + public function findDocsComments(int $interval): array + { + $sql = "SELECT bugdb_comments.reporter_name, COUNT(*) as count + FROM bugdb_comments, bugdb + WHERE comment_type = 'log' + AND (package_name IN ('Doc Build problem', 'Documentation problem', 'Translation problem', 'Online Doc Editor problem') OR bug_type = 'Documentation Problem') + AND comment LIKE '%+Status: Closed%' + AND date_sub(curdate(), INTERVAL ? DAY) <= ts + AND bugdb.id = bugdb_comments.bug + GROUP BY bugdb_comments.reporter_name + ORDER BY count DESC + "; + + $statement = $this->dbh->prepare($sql); + $statement->execute([$interval]); + + return $statement->fetchAll(); + } } diff --git a/www/api.php b/www/api.php index 4a07baf2..4fbf5bc1 100644 --- a/www/api.php +++ b/www/api.php @@ -5,6 +5,9 @@ The API itself will probably be abandoned in the future, but here's the current URL: - https://bugs.php.net/api.php?type=docs&action=closed&interval=7 */ + +use App\Repository\CommentRepository; + require_once '../include/prepend.php'; $type = isset($_GET['type']) ? $_GET['type'] : 'unknown'; @@ -12,22 +15,10 @@ $interval = isset($_GET['interval']) ? (int) $_GET['interval'] : 7; if ($type === 'docs' && $action === 'closed' && $interval) { - - $query = - " - SELECT bugdb_comments.reporter_name, COUNT(*) as count - FROM bugdb_comments, bugdb - WHERE comment_type = 'log' - AND (package_name IN ('Doc Build problem', 'Documentation problem', 'Translation problem', 'Online Doc Editor problem') OR bug_type = 'Documentation Problem') - AND comment LIKE '%+Status: Closed%' - AND date_sub(curdate(), INTERVAL {$interval} DAY) <= ts - AND bugdb.id = bugdb_comments.bug - GROUP BY bugdb_comments.reporter_name - ORDER BY count DESC - "; + $commentRepository = new CommentRepository($dbh); + $rows = $commentRepository->findDocsComments($interval); //@todo add error handling - $rows = $dbh->prepare($query)->execute([])->fetchAll(); if (!$rows) { echo 'The fail train has arrived.'; exit; From 9a8dbe29c491d31872421f91807667473fedf248 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 19 Dec 2018 03:11:46 +0100 Subject: [PATCH 166/277] Add Vote repository class --- src/Repository/VoteRepository.php | 42 +++++++++++++++++++++++++++++++ www/vote.php | 7 ++---- 2 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 src/Repository/VoteRepository.php diff --git a/src/Repository/VoteRepository.php b/src/Repository/VoteRepository.php new file mode 100644 index 00000000..e832e7c3 --- /dev/null +++ b/src/Repository/VoteRepository.php @@ -0,0 +1,42 @@ +dbh = $dbh; + } + + /** + * Find vote row by bug id and IP. + */ + public function findOneByIdAndIp(int $id, string $ip): array + { + $sql = 'SELECT bug, ip FROM bugdb_votes WHERE bug = ? AND ip = ? LIMIT 1'; + + $statement = $this->dbh->prepare($sql); + $statement->execute([$id, $ip]); + + $result = $statement->fetch(); + + if ($result === false) { + return []; + } + + return $result; + } +} diff --git a/www/vote.php b/www/vote.php index 73f2a9ca..e5d6817c 100644 --- a/www/vote.php +++ b/www/vote.php @@ -1,6 +1,7 @@ prepare("SELECT bug, ip FROM bugdb_votes WHERE bug = ? AND ip = ? LIMIT 1") - ->execute([$id, $ip]) - ->fetch(\PDO::FETCH_BOTH); - -if (empty($bug_check)) { +if (empty((new VoteRepository($dbh))->findOneByIdAndIp($id, $ip))) { // If the user vote isn't found, create one. $dbh->prepare(" INSERT INTO bugdb_votes (bug, ip, score, reproduced, tried, sameos, samever) From 1c04d30518e249088ae4e070bebbaaeb03de9355 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 18 Dec 2018 01:47:55 +0100 Subject: [PATCH 167/277] Remove display_bug_success() function The display_bug_success() is a simple wrapper around the echo and has HTML embedded in it. --- include/functions.php | 12 ------------ www/bug-pwd-finder.php | 2 +- www/bug.php | 20 ++++++++++---------- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/include/functions.php b/include/functions.php index 767a9298..5220f2ef 100644 --- a/include/functions.php +++ b/include/functions.php @@ -696,18 +696,6 @@ function display_bug_error($in, $class = 'errors', $head = 'ERROR:') return true; } -/** - * Prints a message saying the action succeeded - * - * @param string $in the string to be displayed - * - * @return void - */ -function display_bug_success($in) -{ - echo "
      {$in}
      \n"; -} - /** * Returns array of changes going to be made */ diff --git a/www/bug-pwd-finder.php b/www/bug-pwd-finder.php index b124b22f..f0aa2ba4 100644 --- a/www/bug-pwd-finder.php +++ b/www/bug-pwd-finder.php @@ -70,7 +70,7 @@ display_bug_error($errors); if ($success) { - display_bug_success($success); + echo '
      '.$success.'
      '; } $_SESSION['answer'] = $captcha->getAnswer(); diff --git a/www/bug.php b/www/bug.php index 3d50389a..0fae8922 100644 --- a/www/bug.php +++ b/www/bug.php @@ -596,34 +596,34 @@ { case 1: case 2: - display_bug_success('The bug was updated successfully.'); + echo '
      The bug was updated successfully.
      '; break; case 3: - display_bug_success('Your comment was added to the bug successfully.'); + echo '
      Your comment was added to the bug successfully.
      '; break; case 4: $bug_url = "{$site_method}://{$site_url}{$basedir}/bug.php?id={$bug_id}"; - display_bug_success(" + echo '
      Thank you for your help! If the status of the bug report you submitted changes, you will be notified. You may return here and check the status or update your report at any time.
      - The URL for your bug report is: {$bug_url}. - "); + The URL for your bug report is: '.$bug_url.'. +
      '; break; case 6: - display_bug_success('Thanks for voting! Your vote should be reflected in the statistics below.'); + echo '
      Thanks for voting! Your vote should be reflected in the statistics below.
      '; break; case 7: - display_bug_success('Your subscribe request has been processed.'); + echo '
      Your subscribe request has been processed.
      '; break; case 8: - display_bug_success('Your unsubscribe request has been processed, please check your email.'); + echo '
      Your unsubscribe request has been processed, please check your email.
      '; break; case 9: - display_bug_success('You have successfully unsubscribed.'); + echo '
      You have successfully unsubscribed.
      '; break; case 10: - display_bug_success('Your vote has been updated.'); + echo '
      Your vote has been updated.
      '; break; default: From 03b4871fa3d916c662b8a88fec03e8a7c2690079 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Thu, 20 Dec 2018 06:29:32 +0100 Subject: [PATCH 168/277] Fix #76720: Contributor aids/readme improvements This fixes mentioned issues together with previous patches done on this file. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 47fd7594..890ec749 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PHP Bug Tracking System +# PHP Bug Tracking System This is a unified bug tracking system for PHP hosted online at [bugs.php.net](https://bugs.php.net). @@ -26,8 +26,8 @@ pear install --alldeps Text_Diff * Database: -Create a new database using `sql/database.sql`, create database schema -`sql/schema.sql` and insert fixtures using `sql/fixtures.sql`. +Create a new MySQL/MariaDB database using `sql/database.sql`, create database +schema `sql/schema.sql` and insert fixtures using `sql/fixtures.sql`. ## Tests From fe4c5e81f570c4dfb9c0bc4530bc3d955ab579d4 Mon Sep 17 00:00:00 2001 From: Kalle Sommer Nielsen Date: Sun, 23 Dec 2018 06:09:07 +0100 Subject: [PATCH 169/277] Minor cleanup --- www/fix.php | 2 -- www/gh-pull-add.php | 3 --- www/patch-add.php | 2 -- www/patch-display.php | 2 -- www/report.php | 7 ++----- www/rpc.php | 2 -- 6 files changed, 2 insertions(+), 16 deletions(-) diff --git a/www/fix.php b/www/fix.php index f1be1dbe..66f552d7 100644 --- a/www/fix.php +++ b/www/fix.php @@ -33,8 +33,6 @@ // If bug exists, continue.. $RESOLVE_REASONS = $FIX_VARIATIONS = $errors = []; -$is_trusted_developer = ($user_flags & BUGS_TRUSTED_DEV); - if ($logged_in != 'developer') { $errors[] = 'The username or password you supplied was incorrect.'; } diff --git a/www/gh-pull-add.php b/www/gh-pull-add.php index 1821d9ea..4e31f5f2 100644 --- a/www/gh-pull-add.php +++ b/www/gh-pull-add.php @@ -38,9 +38,6 @@ $package_name = $buginfo['package_name']; -// Authenticate -$is_trusted_developer = ($user_flags & BUGS_TRUSTED_DEV); - // captcha is not necessary if the user is logged in if (!$logged_in) { $captcha = new Captcha(); diff --git a/www/patch-add.php b/www/patch-add.php index f0eaf26e..6be26ba7 100644 --- a/www/patch-add.php +++ b/www/patch-add.php @@ -44,8 +44,6 @@ $package_name = $buginfo['package_name']; -$is_trusted_developer = ($user_flags & BUGS_TRUSTED_DEV); - // captcha is not necessary if the user is logged in if (!$logged_in) { $captcha = new Captcha(); diff --git a/www/patch-display.php b/www/patch-display.php index 863c3861..57dd490b 100644 --- a/www/patch-display.php +++ b/www/patch-display.php @@ -26,8 +26,6 @@ exit; } -$is_trusted_developer = ($user_flags & BUGS_TRUSTED_DEV); - $canpatch = ($logged_in == 'developer'); $revision = isset($_GET['revision']) ? $_GET['revision'] : null; diff --git a/www/report.php b/www/report.php index 408dfdf0..49fb1f97 100644 --- a/www/report.php +++ b/www/report.php @@ -22,9 +22,6 @@ // Authenticate bugs_authenticate($user, $pw, $logged_in, $user_flags); -$is_trusted_developer = ($user_flags & BUGS_TRUSTED_DEV); -$is_security_developer = ($user_flags & BUGS_SECURITY_DEV); - require "{$ROOT_DIR}/include/php_versions.php"; // captcha is not necessary if the user is logged in @@ -74,7 +71,7 @@ $where_clause = "WHERE package_name != 'Feature/Change Request'"; - if (!$is_security_developer) { + if (!($user_flags & BUGS_SECURITY_DEV)) { $where_clause .= " AND private = 'N' "; } @@ -87,7 +84,7 @@ $possible_duplicates = $dbh->prepare($query)->execute()->fetchAll(); - if (count($possible_duplicates) == 0) { + if (!$possible_duplicates) { $ok_to_submit_report = true; } else { response_header("Report - Confirm", $packageAffectedScript); diff --git a/www/rpc.php b/www/rpc.php index 1430c7a8..dca690a6 100644 --- a/www/rpc.php +++ b/www/rpc.php @@ -31,8 +31,6 @@ bugs_authenticate($user, $pwd, $logged_in, $user_flags); -$is_trusted_developer = ($user_flags & BUGS_TRUSTED_DEV); - if (empty($auth_user->handle)) { echo json_encode(['result' => ['error' => 'Invalid user or password']]); exit; From 402a43856329884eae5b58e83b656b08a6c2ab01 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 24 Dec 2018 03:00:41 +0100 Subject: [PATCH 170/277] Fix strict types issues This makes sure arrays are always returned when calling PDOStatement::fetch() in current repository classes. --- src/Repository/BugRepository.php | 4 +++- src/Repository/VoteRepository.php | 6 +----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Repository/BugRepository.php b/src/Repository/BugRepository.php index 5ef08f3f..d47f0c3c 100644 --- a/src/Repository/BugRepository.php +++ b/src/Repository/BugRepository.php @@ -51,7 +51,9 @@ public function findOneById(int $id): array $statement = $this->dbh->prepare($sql); $statement->execute([$id]); - return $statement->fetch(); + $result = $statement->fetch(); + + return $result === false ? [] : $result; } /** diff --git a/src/Repository/VoteRepository.php b/src/Repository/VoteRepository.php index e832e7c3..1b2aecce 100644 --- a/src/Repository/VoteRepository.php +++ b/src/Repository/VoteRepository.php @@ -33,10 +33,6 @@ public function findOneByIdAndIp(int $id, string $ip): array $result = $statement->fetch(); - if ($result === false) { - return []; - } - - return $result; + return $result === false ? [] : $result; } } From b78a07146df1fdf804df21688a98d98273f12b6b Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 26 Dec 2018 22:27:50 +0100 Subject: [PATCH 171/277] Refactor repository classes to use vanilla PDOStatement Current goal is to use as close to PDO expected functionality without overriding and making custom methods due to easier understanding of the app itself for people working on the code and contributors. This patch refactors repository classes to use the expected \PDOStatement without method chaining. --- src/Repository/ObsoletePatchRepository.php | 10 ++++++++-- src/Repository/PackageRepository.php | 9 +++++++-- src/Repository/PatchRepository.php | 20 ++++++++++++++++---- src/Repository/PullRequestRepository.php | 5 ++++- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/Repository/ObsoletePatchRepository.php b/src/Repository/ObsoletePatchRepository.php index 2682cee0..f8ff1ae5 100644 --- a/src/Repository/ObsoletePatchRepository.php +++ b/src/Repository/ObsoletePatchRepository.php @@ -31,7 +31,10 @@ public function findObsoletingPatches(int $bugId, string $patch, int $revision): WHERE bugdb_id = ? AND obsolete_patch = ? AND obsolete_revision = ? '; - return $this->dbh->prepare($sql)->execute([$bugId, $patch, $revision])->fetchAll(); + $statement = $this->dbh->prepare($sql); + $statement->execute([$bugId, $patch, $revision]); + + return $statement->fetchAll(); } /** @@ -44,6 +47,9 @@ public function findObsoletePatches(int $bugId, string $patch, int $revision): a WHERE bugdb_id = ? AND patch = ? AND revision = ? '; - return $this->dbh->prepare($sql)->execute([$bugId, $patch, $revision])->fetchAll(); + $statement = $this->dbh->prepare($sql); + $statement->execute([$bugId, $patch, $revision]); + + return $statement->fetchAll(); } } diff --git a/src/Repository/PackageRepository.php b/src/Repository/PackageRepository.php index aaaa399c..ae64a1a1 100644 --- a/src/Repository/PackageRepository.php +++ b/src/Repository/PackageRepository.php @@ -46,7 +46,9 @@ public function findAll(string $project = ''): array $sql .= ' ORDER BY parent, disabled, id'; - $data = $this->dbh->prepare($sql)->execute($arguments)->fetchAll(); + $statement = $this->dbh->prepare($sql); + $statement->execute($arguments); + $data = $statement->fetchAll(); return $this->getNested($data); } @@ -67,7 +69,10 @@ public function findEnabled(string $project = ''): array $sql .= ' ORDER BY parent, id'; - $data = $this->dbh->prepare($sql)->execute($arguments)->fetchAll(); + $statement = $this->dbh->prepare($sql); + $statement->execute($arguments); + + $data = $statement->fetchAll(); return $this->getNested($data); } diff --git a/src/Repository/PatchRepository.php b/src/Repository/PatchRepository.php index 971c1ffc..9fd9031c 100644 --- a/src/Repository/PatchRepository.php +++ b/src/Repository/PatchRepository.php @@ -39,7 +39,10 @@ public function findAllByBugId(int $bugId): array ORDER BY revision DESC '; - return $this->dbh->prepare($sql)->execute([$bugId])->fetchAll(); + $statement = $this->dbh->prepare($sql); + $statement->execute([$bugId]); + + return $statement->fetchAll(); } /** @@ -54,7 +57,10 @@ public function findDeveloper(int $bugId, string $patch, int $revision): string $arguments = [$bugId, $patch, $revision]; - return $this->dbh->prepare($sql)->execute($arguments)->fetch(\PDO::FETCH_NUM)[0]; + $statement = $this->dbh->prepare($sql); + $statement->execute($arguments); + + return $statement->fetch(\PDO::FETCH_NUM)[0]; } /** @@ -68,7 +74,10 @@ public function findRevisions(int $bugId, string $patch): array ORDER BY revision DESC '; - return $this->dbh->prepare($sql)->execute([$bugId, $patch])->fetchAll(); + $statement = $this->dbh->prepare($sql); + $statement->execute([$bugId, $patch]); + + return $statement->fetchAll(); } /** @@ -81,7 +90,10 @@ public function getPatchContents(int $bugId, string $name, int $revision): strin WHERE bugdb_id = ? AND patch = ? AND revision = ? '; - if ($this->dbh->prepare($sql)->execute([$bugId, $name, $revision])->fetch(\PDO::FETCH_NUM)[0]) { + $statement = $this->dbh->prepare($sql); + $statement->execute([$bugId, $name, $revision]); + + if ($statement->fetch(\PDO::FETCH_NUM)[0]) { $contents = @file_get_contents($this->getPatchPath($bugId, $name, $revision)); if (!$contents) { diff --git a/src/Repository/PullRequestRepository.php b/src/Repository/PullRequestRepository.php index 89a72cee..0d6bd7e5 100644 --- a/src/Repository/PullRequestRepository.php +++ b/src/Repository/PullRequestRepository.php @@ -35,6 +35,9 @@ public function findAllByBugId(int $bugId) ORDER BY github_repo, github_pull_id DESC '; - return $this->dbh->prepare($sql)->execute([$bugId])->fetchAll(); + $statement = $this->dbh->prepare($sql); + $statement->execute([$bugId]); + + return $statement->fetchAll(); } } From 422f6e5c3e42770374289ac72a4a25ceca1b6c52 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Thu, 27 Dec 2018 12:56:15 +0100 Subject: [PATCH 172/277] Fix #77355: Filing a ticket links to the Edit tab of potential duplicates We should link to the ticket generally, instead of the Edit tab. --- www/report.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/report.php b/www/report.php index 49fb1f97..b5e4ebe0 100644 --- a/www/report.php +++ b/www/report.php @@ -127,7 +127,7 @@ $summary = substr(trim($summary), 0, 256) . ' ...'; } - $bug_url = "bug.php?id={$row['id']}&edit=2"; + $bug_url = "bug.php?id={$row['id']}"; $sdesc = htmlspecialchars($row['sdesc']); $summary = htmlspecialchars($summary); From 14c6ce8cd5511224a9435e42d668ab7f51c1e6ae Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 2 Jan 2019 20:54:32 +0100 Subject: [PATCH 173/277] Bump Composer dependencies - PHPUnit 7.4 upgraded to 7.5 - composer update --- composer.json | 2 +- composer.lock | 85 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/composer.json b/composer.json index 39c9f3de..a4fea73e 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "ext-session": "*" }, "require-dev": { - "phpunit/phpunit": "^7.4" + "phpunit/phpunit": "^7.5" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 7d273408..5296c0b1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dc3b11918cde7a2af15820af62cb92af", + "content-hash": "c4d7d1e13a174de9ebf8016cf7872528", "packages": [], "packages-dev": [ { @@ -680,16 +680,16 @@ }, { "name": "phpunit/phpunit", - "version": "7.4.5", + "version": "7.5.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "61d34e8dd6eb3555900f0f2a2fa9e7e570730102" + "reference": "c23d78776ad415d5506e0679723cb461d71f488f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/61d34e8dd6eb3555900f0f2a2fa9e7e570730102", - "reference": "61d34e8dd6eb3555900f0f2a2fa9e7e570730102", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c23d78776ad415d5506e0679723cb461d71f488f", + "reference": "c23d78776ad415d5506e0679723cb461d71f488f", "shasum": "" }, "require": { @@ -734,7 +734,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.4-dev" + "dev-master": "7.5-dev" } }, "autoload": { @@ -760,7 +760,7 @@ "testing", "xunit" ], - "time": "2018-12-03T05:01:24+00:00" + "time": "2018-12-12T07:20:32+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1325,6 +1325,64 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2016-10-03T07:35:21+00:00" }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.10.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com/", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2018-08-06T14:22:27+00:00" + }, { "name": "theseer/tokenizer", "version": "1.1.0", @@ -1367,20 +1425,21 @@ }, { "name": "webmozart/assert", - "version": "1.3.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" }, "require-dev": { "phpunit/phpunit": "^4.6", @@ -1413,7 +1472,7 @@ "check", "validate" ], - "time": "2018-01-29T19:49:41+00:00" + "time": "2018-12-25T11:19:39+00:00" } ], "aliases": [], From 068d8514af650469cdc566e4bbcd50d52cd5f7e5 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 17 Dec 2018 17:28:48 +0100 Subject: [PATCH 174/277] Add template engine This patch adds an initial simplistic template engine to separate logic from the presentation. Basic initial features: - escaping via Context::noHtml() and Context::e() methods - blocks - nesting options using includes and extending layouts - PHP syntax - variable scopes dedicated to template scope only - Appending blocks (when JS files are in need to be appended) - initial unit and functional tests - Main index page refactored as an example of usage - Very short intro docs how to use the template layer - Thanks to @nhlm for the code review and numerous suggestions to improve the usability and code stability, - Thanks to @KalleZ and for the code review and numerous common sense suggestions about templates themselves. - Thanks to @Maikuolan for the code review and numerous suggestions about the usability. - Moved hash ids redirection to aseparate JavaScript file - Use location instead of window.location in the JavaScript redirection Discussions: - http://news.php.net/php.webmaster/27603 - https://github.com/php/web-bugs/pull/66 --- README.md | 5 + docs/README.md | 3 + docs/templates.md | 197 ++++++++++++++++++ include/prepend.php | 9 + src/Template/Context.php | 178 ++++++++++++++++ src/Template/Engine.php | 161 ++++++++++++++ templates/layout.php | 80 +++++++ templates/pages/index.php | 76 +++++++ tests/Template/ContextTest.php | 78 +++++++ tests/Template/EngineTest.php | 186 +++++++++++++++++ tests/fixtures/templates/base.php | 8 + tests/fixtures/templates/forms/form.php | 8 + tests/fixtures/templates/includes/banner.php | 3 + tests/fixtures/templates/includes/base.php | 3 + tests/fixtures/templates/includes/extends.php | 5 + .../fixtures/templates/includes/variable.php | 3 + tests/fixtures/templates/layout.php | 17 ++ .../fixtures/templates/pages/add_function.php | 5 + tests/fixtures/templates/pages/appending.php | 7 + .../fixtures/templates/pages/assignments.php | 6 + tests/fixtures/templates/pages/extends.php | 5 + tests/fixtures/templates/pages/including.php | 5 + .../templates/pages/invalid_variables.php | 1 + tests/fixtures/templates/pages/no_layout.rss | 19 ++ tests/fixtures/templates/pages/overrides.php | 6 + tests/fixtures/templates/pages/view.php | 9 + www/index.php | 163 +++++---------- www/js/redirect.js | 18 ++ 28 files changed, 1157 insertions(+), 107 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/templates.md create mode 100644 src/Template/Context.php create mode 100644 src/Template/Engine.php create mode 100644 templates/layout.php create mode 100644 templates/pages/index.php create mode 100644 tests/Template/ContextTest.php create mode 100644 tests/Template/EngineTest.php create mode 100644 tests/fixtures/templates/base.php create mode 100644 tests/fixtures/templates/forms/form.php create mode 100644 tests/fixtures/templates/includes/banner.php create mode 100644 tests/fixtures/templates/includes/base.php create mode 100644 tests/fixtures/templates/includes/extends.php create mode 100644 tests/fixtures/templates/includes/variable.php create mode 100644 tests/fixtures/templates/layout.php create mode 100644 tests/fixtures/templates/pages/add_function.php create mode 100644 tests/fixtures/templates/pages/appending.php create mode 100644 tests/fixtures/templates/pages/assignments.php create mode 100644 tests/fixtures/templates/pages/extends.php create mode 100644 tests/fixtures/templates/pages/including.php create mode 100644 tests/fixtures/templates/pages/invalid_variables.php create mode 100644 tests/fixtures/templates/pages/no_layout.rss create mode 100644 tests/fixtures/templates/pages/overrides.php create mode 100644 tests/fixtures/templates/pages/view.php create mode 100644 www/js/redirect.js diff --git a/README.md b/README.md index 890ec749..526ee6ae 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Source code of this application is structured in the following directories: ```bash / ├─ .git/ # Git configuration and source directory + ├─ docs/ # Application documentation └─ include/ # Application helper functions and configuration ├─ classes/ # PEAR class overrides ├─ prepend.php # Autoloader, DB connection, container, app initialization @@ -103,3 +104,7 @@ git remote add upstream git://github.com/php/web-bugs git config branch.master.remote upstream git pull --rebase ``` + +## Documentation + +More information about this application can be found in the [documentation](/docs). diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..1cc4c00f --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# Application documentation + +* [Templates](/docs/templates.md) diff --git a/docs/templates.md b/docs/templates.md new file mode 100644 index 00000000..66ca34d7 --- /dev/null +++ b/docs/templates.md @@ -0,0 +1,197 @@ +# Templates + +A simple template engine separates logic from the presentation and provides +methods for creating nested templates and escaping strings to protect against +too common XSS vulnerabilities. + +It is initialized in the application bootstrap: + +```php +$template = new App\Template\Engine(__DIR__.'/../path/to/templates'); +``` + +Site-wide configuration parameters can be assigned before rendering so they are +available in all templates: + +```php +$template->assign([ + 'siteUrl' => 'https://bugs.php.net/', + // ... +]); +``` + +Page can be rendered in the controller: + +```php +echo $template->render('pages/how_to_report.php', [ + 'mainHeading' => 'How to report a bug?', +]); +``` + +The `templates/pages/how_to_report.php`: + +```php +extends('layout.php', ['title' => 'Reporting bugs']) ?> + +start('main_content') ?> +

      noHtml($mainHeading) ?>

      + +

      +end('main_content') ?> + +start('scripts') ?> + +end('scripts') ?> +``` + +The `templates/layout.php`: + +```html + + + + + + PHP Bug Tracking System :: <?= $title ?? '' ?> + + + block('main_content') ?> + +
      + + + block('scripts') ?> + + +``` + +## Including templates + +To include a partial template snippet file: + +```php +include('forms/report_bug.php') ?> +``` + +which is equivalent to ``. +The variable scope is inherited by the template that included the file. + +## Blocks + +Blocks are main building elements that contain template snippets and can be +included into the parent file(s). + +Block is started with the `$this->start('block_name')` call and ends with +`$this->end('block_name')`: + +```php +start('block_name') ?> +

      Heading

      + +

      ...

      +end('block_name') ?> +``` + +### Appending blocks + +Block content can be appended to existing blocks by the +`$this->append('block_name')`. + +The `templates/layout.php`: + +```html + + + + block('content'); ?> + + block('scripts'); ?> + + +``` + +The `templates/pages/index.php`: + +```php +extends('layout.php'); ?> + +start('scripts'); ?> + +end('scripts'); ?> + +start('content') ?> + include('forms/form.php') ?> +end('content') ?> +``` + +The `templates/forms/form.php`: + +```php + + + + + +append('scripts'); ?> + +end('scripts'); ?> +``` + +The final rendered page: + +```html + + + +
      + + +
      + + + + + +``` + +## Helpers + +Registering additional template helpers can be useful when a custom function or +class method needs to be called in the template. + +### Registering function + +```php +$template->register('formatDate', function (int $timestamp): string { + return gmdate('Y-m-d H:i e', $timestamp - date('Z', $timestamp)); +}); +``` + +### Registering object method + +```php +$template->register('doSomething', [$object, 'methodName']); +``` + +Using helpers in templates: + +```php +

      Time: formatDate(time()) ?>

      +
      doSomething('arguments') ?>
      +``` + +## Escaping + +When protecting against XSS there are two built-in methods provided. + +To replace all characters to their applicable HTML entities in the given string: + +```php +noHtml($var) ?> +``` + +To escape given string and still preserve certain characters as HTML: + +```php +e($var) ?> +``` diff --git a/include/prepend.php b/include/prepend.php index 388ebb17..5b94bf38 100644 --- a/include/prepend.php +++ b/include/prepend.php @@ -2,6 +2,7 @@ use App\Autoloader; use App\Database\Statement; +use App\Template\Engine; // Dual PSR-4 compatible class autoloader. When Composer is not available, an // application specific replacement class is used. Once Composer can be added @@ -83,3 +84,11 @@ // Last Updated.. $tmp = filectime($_SERVER['SCRIPT_FILENAME']); $LAST_UPDATED = date('D M d H:i:s Y', $tmp - date('Z', $tmp)) . ' UTC'; + +// Initialize template engine. +$template = new Engine(__DIR__.'/../templates'); +$template->assign([ + 'lastUpdated' => $LAST_UPDATED, + 'siteScheme' => $site_method, + 'siteUrl' => $site_url, +]); diff --git a/src/Template/Context.php b/src/Template/Context.php new file mode 100644 index 00000000..eaf94032 --- /dev/null +++ b/src/Template/Context.php @@ -0,0 +1,178 @@ +method(). + */ +class Context +{ + /** + * Templates directory. + * + * @var string + */ + private $dir; + + /** + * The current processed template or snippet file. + * + * @var string + */ + private $current; + + /** + * All assigned and set variables for the template. + * + * @var array + */ + private $variables = []; + + /** + * Pool of blocks for the template context. + * + * @var array + */ + private $blocks = []; + + /** + * Parent templates extended by child templates. + * + * @var array + */ + public $tree = []; + + /** + * Registered callables. + * + * @var array + */ + private $callables = []; + + /** + * Current nesting level of the output buffering mechanism. + * + * @var int + */ + private $bufferLevel = 0; + + /** + * Class constructor. + */ + public function __construct( + string $dir, + array $variables = [], + array $callables = [] + ) { + $this->dir = $dir; + $this->variables = $variables; + $this->callables = $callables; + } + + /** + * Sets a parent layout for the given template. Additional variables in the + * parent scope can be defined via the second argument. + */ + public function extends(string $parent, array $variables = []): void + { + if (isset($this->tree[$this->current])) { + throw new \Exception('Extending '.$parent.' is not possible.'); + } + + $this->tree[$this->current] = [$parent, $variables]; + } + + /** + * Return a block content from the pool by name. + */ + public function block(string $name): string + { + return $this->blocks[$name] ?? ''; + } + + /** + * Starts a new template block. Under the hood a simple separate output + * buffering is used to capture the block content. Content can be also + * appended to previously set same block name. + */ + public function start(string $name): void + { + $this->blocks[$name] = ''; + + ++$this->bufferLevel; + + ob_start(); + } + + /** + * Append content to a template block. If no block with the key name exists + * yet it starts a new one. + */ + public function append(string $name): void + { + if (!isset($this->blocks[$name])) { + $this->blocks[$name] = ''; + } + + ++$this->bufferLevel; + + ob_start(); + } + + /** + * Ends block output buffering and stores its content into the pool. + */ + public function end(string $name): void + { + --$this->bufferLevel; + + $content = ob_get_clean(); + + if (!empty($this->blocks[$name])) { + $this->blocks[$name] .= $content; + } else { + $this->blocks[$name] = $content; + } + } + + /** + * Include template file into existing template. + * + * @return mixed + */ + public function include(string $template) + { + return include $this->dir.'/'.$template; + } + + /** + * Scalpel when preventing XSS vulnerabilities. This escapes given string + * and still preserves certain characters as HTML. + */ + public function e(string $string): string + { + return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); + } + + /** + * Hammer when protecting against XSS. Sanitize strings and replace all + * characters to their applicable HTML entities from it. + */ + public function noHtml(string $string): string + { + return htmlentities($string, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + } + + /** + * A proxy to call registered callable. + * + * @return mixed + */ + public function __call(string $method, array $arguments) + { + if (isset($this->callables[$method])) { + return call_user_func_array($this->callables[$method], $arguments); + } + } +} diff --git a/src/Template/Engine.php b/src/Template/Engine.php new file mode 100644 index 00000000..4fee288a --- /dev/null +++ b/src/Template/Engine.php @@ -0,0 +1,161 @@ +dir = $dir; + } + + /** + * This enables assigning new variables to the template scope right after + * initializing a template engine. Some variables in templates are like + * parameters or globals and should be added only on one place instead of + * repeating them at each ...->render() call. + */ + public function assign(array $variables = []): void + { + $this->variables = array_replace($this->variables, $variables); + } + + /** + * Get assigned variables of the template. + */ + public function getVariables(): array + { + return $this->variables; + } + + /** + * Add new template helper function as a callable defined in the (front) + * controller to the template scope. + */ + public function register(string $name, callable $callable): void + { + if (method_exists(Context::class, $name)) { + throw new \Exception( + $name.' is already registered by the template engine. Use a different name.' + ); + } + + $this->callables[$name] = $callable; + } + + /** + * Renders given template file and populates its scope with variables + * provided as array elements. Each array key is a variable name in template + * scope and array item value is set as a variable value. + */ + public function render(string $template, array $variables = []): string + { + $variables = array_replace($this->variables, $variables); + + $this->context = new Context( + $this->dir, + $variables, + $this->callables + ); + + $buffer = $this->bufferize($template, $variables); + + while (!empty($current = array_shift($this->context->tree))) { + $buffer = trim($buffer); + $buffer .= $this->bufferize($current[0], $current[1]); + } + + return $buffer; + } + + /** + * Processes given template file, merges variables into template scope using + * output buffering and returns the rendered content string. Note that $this + * pseudo-variable in the closure refers to the scope of the Context class. + */ + private function bufferize(string $template, array $variables = []): string + { + if (!is_file($this->dir.'/'.$template)) { + throw new \Exception($template.' is missing or not a valid template.'); + } + + $closure = \Closure::bind( + function ($template, $variables) { + $this->current = $template; + $this->variables = array_replace($this->variables, $variables); + unset($variables, $template); + + if (count($this->variables) > extract($this->variables, EXTR_SKIP)) { + throw new \Exception( + 'Variables with numeric names $0, $1... cannot be imported to scope '.$this->current + ); + } + + ++$this->bufferLevel; + + ob_start(); + + try { + include $this->dir.'/'.$this->current; + } catch (\Exception $e) { + // Close all opened buffers + while ($this->bufferLevel > 0) { + --$this->bufferLevel; + + ob_end_clean(); + } + + throw $e; + } + + --$this->bufferLevel; + + return ob_get_clean(); + }, + $this->context, + Context::class + ); + + return $closure($template, $variables); + } +} diff --git a/templates/layout.php b/templates/layout.php new file mode 100644 index 00000000..a0eb35f5 --- /dev/null +++ b/templates/layout.php @@ -0,0 +1,80 @@ + + + + + PHP :: <?= $this->e($title) ?> + + + + + + + + + + + + + + +
      + php.net |  + support |  + documentation |  + report a bug |  + advanced search |  + search howto |  + statistics |  + random bug |  + + my bugs |  + + admin |  + + logout + + login + +
      + + + + + +
      + block('content') ?> +
      + + + + + + + + + + +
       
      + + PHP + Copyright © 2001- The PHP Group
      + All rights reserved. +
      +
      + Last updated: +
      + +block('scripts') ?> + + diff --git a/templates/pages/index.php b/templates/pages/index.php new file mode 100644 index 00000000..177e93f2 --- /dev/null +++ b/templates/pages/index.php @@ -0,0 +1,76 @@ +extends('layout.php', ['title' => 'Bugs homepage']) ?> + +start('content') ?> + +

      PHP Bug Tracking System

      + +

      Before you report a bug, please make sure you have completed the following +steps:

      + + + +

      Once you've double-checked that the bug you've found hasn't already been +reported, and that you have collected all the information you need to file an +excellent bug report, you can do so on our bug reporting +page.

      + +

      Search the Bug System

      + +

      You can search all of the bugs that have been reported on our +advanced search page, or use the form at the top of the +page for a basic default search. Read the search howto +for instructions on how search works.

      + +

      If you have 10 minutes to kill and you want to help us out, grab a random +open bug and see if you can help resolve it. We have made it easy. Hit +random to go directly to a random open bug.

      + +

      Common searches

      + + + +

      Bug System Statistics

      + +

      You can view a variety of statistics about the bugs that have been reported +on our bug statistics page.

      + +end('content') ?> + +start('scripts') ?> + +end('scripts') ?> diff --git a/tests/Template/ContextTest.php b/tests/Template/ContextTest.php new file mode 100644 index 00000000..5092f696 --- /dev/null +++ b/tests/Template/ContextTest.php @@ -0,0 +1,78 @@ +context = new Context(__DIR__.'/../fixtures/templates'); + } + + public function testBlock() + { + $this->context->start('foo'); + echo 'bar'; + $this->context->end('foo'); + + $this->assertEquals($this->context->block('foo'), 'bar'); + + $this->context->append('foo'); + echo 'baz'; + $this->context->end('foo'); + + $this->assertEquals($this->context->block('foo'), 'barbaz'); + + $this->context->start('foo'); + echo 'overridden'; + $this->context->end('foo'); + + $this->assertEquals($this->context->block('foo'), 'overridden'); + } + + public function testInclude() + { + ob_start(); + $this->context->include('includes/banner.php'); + $content = ob_get_clean(); + + $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/templates/includes/banner.php'), $content); + } + + public function testIncludeReturn() + { + $variable = $this->context->include('includes/variable.php'); + + $this->assertEquals(include __DIR__.'/../fixtures/templates/includes/variable.php', $variable); + } + + /** + * @dataProvider attacksProvider + */ + public function testEscaping($malicious, $escaped, $noHtml) + { + $this->assertEquals($escaped, $this->context->e($malicious)); + } + + /** + * @dataProvider attacksProvider + */ + public function testNoHtml($malicious, $escaped, $noHtml) + { + $this->assertEquals($noHtml, $this->context->noHtml($malicious)); + } + + public function attacksProvider() + { + return [ + [ + '', + '<iframe src="javascript:alert('Xss')";></iframe>', + '<iframe src="javascript:alert('Xss')";></iframe>' + ] + ]; + } +} diff --git a/tests/Template/EngineTest.php b/tests/Template/EngineTest.php new file mode 100644 index 00000000..16174b56 --- /dev/null +++ b/tests/Template/EngineTest.php @@ -0,0 +1,186 @@ +template = new Engine(__DIR__.'/../fixtures/templates'); + } + + public function testView() + { + $content = $this->template->render('pages/view.php', [ + 'foo' => 'Lorem ipsum dolor sit amet.', + 'sidebar' => 'PHP is a popular general-purpose scripting language that is especially suited to web development', + ]); + + $this->assertRegexp('/Lorem ipsum dolor sit amet/', $content); + $this->assertRegexp('/PHP is a popular general-purpose/', $content); + } + + public function testRegisterNew() + { + // Register callable function + $this->template->register('addAsterisks', function ($var) { + return '***'.$var.'***'; + }); + + // Register callable object and method + $object = new class { + public $property; + + public function doSomething($argument) {} + }; + $this->template->register('doSomething', [$object, 'doSomething']); + + $content = $this->template->render('pages/add_function.php', [ + 'foo' => 'Lorem ipsum dolor sit amet.', + ]); + + $this->assertRegexp('/\*\*\*Lorem ipsum dolor sit amet\.\*\*\*/', $content); + } + + public function testRegisterExisting() + { + $this->expectException(\Exception::class); + + $this->template->register('noHtml', function ($var) { + return $var; + }); + } + + public function testAssignments() + { + $this->template->assign([ + 'parameter' => 'FooBarBaz', + ]); + + $content = $this->template->render('pages/assignments.php', [ + 'foo' => 'Lorem ipsum dolor sit amet.', + ]); + + $this->assertRegexp('/Lorem ipsum dolor sit amet\./', $content); + $this->assertRegexp('/FooBarBaz/', $content); + } + + public function testMerge() + { + $this->template->assign([ + 'foo', + 'bar', + 'qux' => 'quux', + ]); + + $this->template->assign([ + 'baz', + 'qux' => 'quuz', + ]); + + $this->assertEquals(['baz', 'bar', 'qux' => 'quuz'], $this->template->getVariables()); + } + + public function testVariablesScope() + { + $this->template->assign([ + 'parameter' => 'Parameter value', + ]); + + $content = $this->template->render('pages/invalid_variables.php', [ + 'foo' => 'Lorem ipsum dolor sit amet', + ]); + + $expected = var_export([ + 'parameter' => 'Parameter value', + 'foo' => 'Lorem ipsum dolor sit amet', + ], true); + + $this->assertEquals($expected, $content); + } + + public function testInvalidVariables() + { + $this->template->assign([ + 'Invalid value with key 0', + 'parameter' => 'Parameter value', + 'Invalid value with key 1', + ]); + + $this->expectException(\Exception::class); + + $content = $this->template->render('pages/invalid_variables.php', [ + 'foo' => 'Lorem ipsum dolor sit amet', + 1 => 'Invalid overridden value with key 1', + ]); + } + + public function testOverrides() + { + $this->template->assign([ + 'pageParameter_1' => 'Page parameter 1', + 'pageParameter_2' => 'Page parameter 2', + 'layoutParameter_1' => 'Layout parameter 1', + 'layoutParameter_2' => 'Layout parameter 2', + 'layoutParameter_3' => 'Layout parameter 3', + ]); + + $content = $this->template->render('pages/overrides.php', [ + 'pageParameter_2' => 'Overridden parameter 2', + 'layoutParameter_2' => 'Layout overridden parameter 2', + ]); + + $this->assertRegexp('/Page parameter 1/', $content); + $this->assertRegexp('/^((?!Page parameter 2).)*$/s', $content); + $this->assertRegexp('/Overridden parameter 2/', $content); + $this->assertRegexp('/Layout parameter 1/', $content); + $this->assertRegexp('/^((?!Layout parameter 2).)*$/s', $content); + $this->assertRegexp('/Layout overridden parameter 2/', $content); + } + + public function testAppending() + { + $content = $this->template->render('pages/appending.php'); + + $this->assertRegexp('/file\_1\.js/', $content); + $this->assertRegexp('/file\_2\.js/', $content); + } + + public function testIncluding() + { + $content = $this->template->render('pages/including.php'); + + $this->assertRegexp('/\
      /', $content); + $this->assertRegexp('/Banner inclusion/', $content); + } + + public function testNoLayout() + { + $content = $this->template->render('pages/no_layout.rss'); + + $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/templates/pages/no_layout.rss'), $content); + } + + public function testMissingTemplate() + { + $this->template->assign([ + 'parameter' => 'Parameter value', + ]); + + $this->expectException(\Exception::class); + + $content = $this->template->render('pages/this/does/not/exist.php', [ + 'foo' => 'Lorem ipsum dolor sit amet', + ]); + } + + public function testExtending() + { + $this->expectException(\Exception::class); + + $html = $this->template->render('pages/extends.php'); + } +} diff --git a/tests/fixtures/templates/base.php b/tests/fixtures/templates/base.php new file mode 100644 index 00000000..8b2ca42e --- /dev/null +++ b/tests/fixtures/templates/base.php @@ -0,0 +1,8 @@ + + + <?=$this->e($title ?? '')?> + + + block('body') ?> + + diff --git a/tests/fixtures/templates/forms/form.php b/tests/fixtures/templates/forms/form.php new file mode 100644 index 00000000..b3c64b0d --- /dev/null +++ b/tests/fixtures/templates/forms/form.php @@ -0,0 +1,8 @@ +append('scripts'); ?> + +end('scripts'); ?> + + + + +
      diff --git a/tests/fixtures/templates/includes/banner.php b/tests/fixtures/templates/includes/banner.php new file mode 100644 index 00000000..9a80ad1c --- /dev/null +++ b/tests/fixtures/templates/includes/banner.php @@ -0,0 +1,3 @@ +

      Banner inclusion

      + +

      Lorem ipsum dolor sit amet

      diff --git a/tests/fixtures/templates/includes/base.php b/tests/fixtures/templates/includes/base.php new file mode 100644 index 00000000..e7c452c7 --- /dev/null +++ b/tests/fixtures/templates/includes/base.php @@ -0,0 +1,3 @@ +
      + block('item') ?> +
      diff --git a/tests/fixtures/templates/includes/extends.php b/tests/fixtures/templates/includes/extends.php new file mode 100644 index 00000000..7bffe991 --- /dev/null +++ b/tests/fixtures/templates/includes/extends.php @@ -0,0 +1,5 @@ +extends('includes/base.php') ?> + +start('item') ?> + +end('item') ?> diff --git a/tests/fixtures/templates/includes/variable.php b/tests/fixtures/templates/includes/variable.php new file mode 100644 index 00000000..eb220945 --- /dev/null +++ b/tests/fixtures/templates/includes/variable.php @@ -0,0 +1,3 @@ +extends('base.php') ?> + +start('body') ?> + block('sidebar') ?> + + block('content') ?> + + block('this_block_is_not_set') ?> + + + + + + block('scripts') ?> + + include('includes/banner.php') ?> +end('body') ?> diff --git a/tests/fixtures/templates/pages/add_function.php b/tests/fixtures/templates/pages/add_function.php new file mode 100644 index 00000000..71413ee2 --- /dev/null +++ b/tests/fixtures/templates/pages/add_function.php @@ -0,0 +1,5 @@ +extends('layout.php', ['title' => 'Bugs homepage']) ?> + +start('content'); ?> +addAsterisks($foo); ?> +end('content'); ?> diff --git a/tests/fixtures/templates/pages/appending.php b/tests/fixtures/templates/pages/appending.php new file mode 100644 index 00000000..193a4b54 --- /dev/null +++ b/tests/fixtures/templates/pages/appending.php @@ -0,0 +1,7 @@ +extends('layout.php', ['title' => 'Testing blocks appends']) ?> + + + +append('scripts'); ?> + +end('scripts'); ?> diff --git a/tests/fixtures/templates/pages/assignments.php b/tests/fixtures/templates/pages/assignments.php new file mode 100644 index 00000000..bddc3a56 --- /dev/null +++ b/tests/fixtures/templates/pages/assignments.php @@ -0,0 +1,6 @@ +extends('layout.php', ['title' => 'Testing variables']) ?> + +start('content'); ?> +Defined parameter is .
      + +end('content'); ?> diff --git a/tests/fixtures/templates/pages/extends.php b/tests/fixtures/templates/pages/extends.php new file mode 100644 index 00000000..4a7f8848 --- /dev/null +++ b/tests/fixtures/templates/pages/extends.php @@ -0,0 +1,5 @@ +extends('layout.php') ?> + +start('content') ?> + include('includes/extends.php') ?> +end('content') ?> diff --git a/tests/fixtures/templates/pages/including.php b/tests/fixtures/templates/pages/including.php new file mode 100644 index 00000000..746b59a4 --- /dev/null +++ b/tests/fixtures/templates/pages/including.php @@ -0,0 +1,5 @@ +extends('layout.php', ['title' => 'Testing blocks appends']) ?> + +start('content') ?> +include('forms/form.php') ?> +end('content') ?> diff --git a/tests/fixtures/templates/pages/invalid_variables.php b/tests/fixtures/templates/pages/invalid_variables.php new file mode 100644 index 00000000..175b090d --- /dev/null +++ b/tests/fixtures/templates/pages/invalid_variables.php @@ -0,0 +1 @@ + diff --git a/tests/fixtures/templates/pages/no_layout.rss b/tests/fixtures/templates/pages/no_layout.rss new file mode 100644 index 00000000..6ef9e6b0 --- /dev/null +++ b/tests/fixtures/templates/pages/no_layout.rss @@ -0,0 +1,19 @@ + + + + RSS Title + This is an example of an RSS feed + https://www.example.com/main.html + Mon, 06 Sep 2010 00:01:00 +0000 + Sun, 06 Sep 2009 16:20:00 +0000 + 1800 + + + Example entry + Here is some text containing an interesting description. + https://www.example.com/blog/post/1 + 7bd204c6-1655-4c27-aeee-53f933c5395f + Sun, 06 Sep 2009 16:20:00 +0000 + + + diff --git a/tests/fixtures/templates/pages/overrides.php b/tests/fixtures/templates/pages/overrides.php new file mode 100644 index 00000000..45192aea --- /dev/null +++ b/tests/fixtures/templates/pages/overrides.php @@ -0,0 +1,6 @@ +extends('layout.php', ['title' => 'Testing variables', 'layoutParameter_3' => 'Layout overridden parameter 3']) ?> + +start('content'); ?> + + +end('content'); ?> diff --git a/tests/fixtures/templates/pages/view.php b/tests/fixtures/templates/pages/view.php new file mode 100644 index 00000000..e20c8a5b --- /dev/null +++ b/tests/fixtures/templates/pages/view.php @@ -0,0 +1,9 @@ +extends('layout.php', ['title' => 'Bugs homepage']) ?> + +start('content'); ?> + +end('content'); ?> + +start('sidebar'); ?> + +end('sidebar'); ?> diff --git a/www/index.php b/www/index.php index 03e40143..413e608a 100644 --- a/www/index.php +++ b/www/index.php @@ -1,123 +1,72 @@ findRandom(); - redirect("bug.php?id={$id[0]}"); -} +// Start session +session_start(); // Authenticate bugs_authenticate($user, $pw, $logged_in, $user_flags); -response_header('Bugs'); - -?> - - - -

      PHP Bug Tracking System

      - -

      Before you report a bug, please make sure you have completed the following steps:

      - -
        -
      • - Used the form above or our advanced search page - to make sure nobody has reported the bug already. -
      • - -
      • - Make sure you are using the latest stable version or a build from Git, if - similar bugs have recently been fixed and committed. -
      • -
      • - Read our tips on how to report a bug that someone will want to help fix. -
      • +$template->assign([ + 'authIsLoggedIn' => $isLoggedIn, + 'authUsername' => $username, + 'authRole' => $logged_in, +]); -
      • - Read the security guidelines, if you think an issue might be security related. -
      • - -
      • - See how to get a backtrace in case of a crash: - for *NIX and - for Windows. -
      • - -
      • - Make sure it isn't a support question. For support, - see the support page. -
      • -
      - -

      Once you've double-checked that the bug you've found hasn't already been -reported, and that you have collected all the information you need to file an -excellent bug report, you can do so on our bug reporting -page.

      - -

      Search the Bug System

      - -

      You can search all of the bugs that have been reported on our -advanced search page, or use the form -at the top of the page for a basic default search. Read the -search howto for instructions on -how search works.

      - -

      If you have 10 minutes to kill and you want to help us out, grab a -random open bug and see if you can help resolve it. We have made it -easy. Hit -:///random to go directly -to a random open bug.

      - -

      Common searches

      -
        - '&bug_type=All', - 'Most recent open bugs (all) with patch or pull request' => '&bug_type=All&patch=Y&pull=Y', - 'Most recent open bugs (PHP 5.6)' => '&bug_type=All&phpver=5.6', - 'Most recent open bugs (PHP 7.1)' => '&bug_type=All&phpver=7.1', - 'Most recent open bugs (PHP 7.2)' => '&bug_type=All&phpver=7.2', - 'Most recent open bugs (PHP 7.3)' => '&bug_type=All&phpver=7.3', - 'Open Documentation bugs' => '&bug_type=Documentation+Problem', - 'Open Documentation bugs (with patches)' => '&bug_type=Documentation+Problem&patch=Y' - ]; - - if (!empty($_SESSION["user"])) { - $searches['Your assigned open bugs'] = '&assign='.urlencode($_SESSION['user']); - } +// If 'id' is passed redirect to the bug page +$id = (int) ($_GET['id'] ?? 0); - foreach ($searches as $title => $sufix) { - echo '
      • ' . $title . '
      • ' . "\n"; - } -?> -
      +if (0 !== $id) { + redirect('bug.php?id='.$id); +} -

      Bug System Statistics

      +if ('/random' === $_SERVER['REQUEST_URI']) { + $id = (new BugRepository($dbh))->findRandom(); + redirect('bug.php?id='.$id[0]); +} -

      You can view a variety of statistics about the bugs that have been -reported on our bug statistics page.

      +$searches = [ + 'Most recent open bugs (all)' => '&bug_type=All', + 'Most recent open bugs (all) with patch or pull request' => '&bug_type=All&patch=Y&pull=Y', + 'Most recent open bugs (PHP 5.6)' => '&bug_type=All&phpver=5.6', + 'Most recent open bugs (PHP 7.1)' => '&bug_type=All&phpver=7.1', + 'Most recent open bugs (PHP 7.2)' => '&bug_type=All&phpver=7.2', + 'Most recent open bugs (PHP 7.3)' => '&bug_type=All&phpver=7.3', + 'Open Documentation bugs' => '&bug_type=Documentation+Problem', + 'Open Documentation bugs (with patches)' => '&bug_type=Documentation+Problem&patch=Y', +]; + +if (!empty($_SESSION['user'])) { + $searches['Your assigned open bugs'] = '&assign='.urlencode($_SESSION['user']); +} -render('pages/index.php', [ + 'searches' => $searches, +]); diff --git a/www/js/redirect.js b/www/js/redirect.js new file mode 100644 index 00000000..fcbcc82d --- /dev/null +++ b/www/js/redirect.js @@ -0,0 +1,18 @@ +'use strict'; + +/** + * Servers can't deal properly with URLs containing hash. This redirects bug id + * passed as #id to the front controller. For example, + * https://bugs.php.net/#12345 + * + * This is implemented for convenience if typo happens when entering bugs.php.net + * url with id, since bugs are prefixed with hashes in bug reporting guidelines, + * PHP commit messages and similar places. + */ + +var bugId = location.hash.substr(1) * 1; + +if (bugId > 0) { + var loc = location; + loc.replace(loc.protocol + '//' + loc.host + (loc.port ? ':' + loc.port : '') + '/' + bugId); +} From 6712d9e13cdc37af96018976ac50b4b1c62b5604 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Fri, 4 Jan 2019 13:04:49 +0100 Subject: [PATCH 175/277] Fix #77403: Issue tracker: Undefined status "Suspended" --- www/search-howto.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/www/search-howto.php b/www/search-howto.php index 10efdfb8..463b79eb 100644 --- a/www/search-howto.php +++ b/www/search-howto.php @@ -92,6 +92,10 @@ closed, duplicates, or not-a-bug. Only developers and the original author can affect this date as public comments do not count. +
    • + Suspended: Tickets which are waiting on some action + which is outside the scope of the PHP developers. +
    • All: All types, even not-a-bug.
    From ae57162e8dce7a000a1e3ea3e05387a9263ef0bc Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 16 Dec 2018 09:45:38 +0100 Subject: [PATCH 176/277] Refactor fetching versions - Procedural code moved to OOP - Added unit tests - Added a more flexible tmp folder location and introduce var folder (in production this won't work yet, so we still use /tmp there). For local development environments var directory in project root is used for faster and easier project setup. - Added initial extensible PSR-16-alike semi-compatible cache class and refactored storing fetched versions. - The versions list generator is now simpler and a bit more logical what is happening. Versions sort order is the same as before. - Added ComposerScripts utility/service class for creating required directories (uploads and var/cache), and configuration file, when installing application in development environment. --- .gitignore | 3 + README.md | 11 +- composer.json | 6 + include/php_versions.php | 106 -------------- src/Utils/Cache.php | 157 +++++++++++++++++++++ src/Utils/ComposerScripts.php | 52 +++++++ src/Utils/Versions/Client.php | 43 ++++++ src/Utils/Versions/Generator.php | 185 +++++++++++++++++++++++++ tests/Utils/CacheTest.php | 41 ++++++ tests/Utils/Versions/ClientTest.php | 36 +++++ tests/Utils/Versions/GeneratorTest.php | 70 ++++++++++ tests/fixtures/versions/versions.php | 4 + tests/mock/responses/dev-body.txt | 1 + tests/mock/responses/stable-body.txt | 1 + www/report.php | 9 +- 15 files changed, 612 insertions(+), 113 deletions(-) delete mode 100644 include/php_versions.php create mode 100644 src/Utils/Cache.php create mode 100644 src/Utils/ComposerScripts.php create mode 100644 src/Utils/Versions/Client.php create mode 100644 src/Utils/Versions/Generator.php create mode 100644 tests/Utils/CacheTest.php create mode 100644 tests/Utils/Versions/ClientTest.php create mode 100644 tests/Utils/Versions/GeneratorTest.php create mode 100644 tests/fixtures/versions/versions.php create mode 100644 tests/mock/responses/dev-body.txt create mode 100644 tests/mock/responses/stable-body.txt diff --git a/.gitignore b/.gitignore index 6c2d0110..07c61bcf 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,8 @@ # Local specific PHPUnit configuration /phpunit.xml +# Transient and temporary generated files +/var/ + # Generated by Composer /vendor/ diff --git a/README.md b/README.md index 526ee6ae..e24aca40 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,6 @@ This is a unified bug tracking system for PHP hosted online at ## Local installation -* Copy configuration and modify it accordingly for your local system: - -```bash -cp local_config.php.sample local_config.php -``` - * Install development dependencies with Composer: ```bash @@ -24,6 +18,10 @@ pear channel-update pear.php.net pear install --alldeps Text_Diff ``` +* Configuration: + +Modify `local_config.php` according to your local development environment. + * Database: Create a new MySQL/MariaDB database using `sql/database.sql`, create database @@ -58,6 +56,7 @@ Source code of this application is structured in the following directories: ├─ templates/ # Application templates ├─ tests/ # Application automated tests ├─ uploads/ # Uploaded patch files + ├─ var/ # Transient and temporary generated files ├─ vendor/ # Dependencies generated by Composer └─ www/ # Publicly accessible directory for online bugs.php.net ├─ css/ # Stylesheets diff --git a/composer.json b/composer.json index a4fea73e..cddad878 100644 --- a/composer.json +++ b/composer.json @@ -42,5 +42,11 @@ "psr-4": { "App\\Tests\\": "tests/" } + }, + "scripts":{ + "post-install-cmd": [ + "App\\Utils\\ComposerScripts::installConfig", + "App\\Utils\\ComposerScripts::createDirectories" + ] } } diff --git a/include/php_versions.php b/include/php_versions.php deleted file mode 100644 index 8aa07af3..00000000 --- a/include/php_versions.php +++ /dev/null @@ -1,106 +0,0 @@ -/tmp/versions.php -the versions are weighted by the following: -- major+minor version desc (7>5.4>5.3>master) -- between a minor version we order by the micro if available: first the qa releases: alpha/beta/rc, then the stable, then the Git versions(snaps, Git) - -Stable releases are pulled from https://php.net/releases/active.php -*/ - -// Custom versions appended to the list -$custom_versions = [ - 'Next Major Version', - 'Next Minor Version', - 'Irrelevant' -]; - -if(!file_exists("/tmp/versions.php") || filemtime("/tmp/versions.php") < $_SERVER['REQUEST_TIME'] - 3600) { - $versions = buildVersions(); - $versions_data = var_export($versions, true); - file_put_contents("/tmp/versions.php", ' $major_releases) { - foreach ($major_releases as $release) { - $version_parts = parseVersion($release['version']); - $versions[$version_parts['major']][$version_parts['minor']][$version_parts['micro']] = $version_parts; - ksort($versions[$version_parts['major']][$version_parts['minor']]); - } - } - - $flat_versions = []; - - // add master to the end of the list - foreach ($default_versions as $default_version) { - $flat_versions[] = 'master-'.$default_version; - } - - // add the fetched versions to the list - foreach ($versions as $major_number => $major) { - foreach ($major as $minor_number => $minor) { - // add the default versions to ever minor branch - foreach ($default_versions as $default_version) { - $flat_versions[] = $major_number.'.'.$minor_number.$default_version; - } - foreach ($minor as $micro_number => $micro) { - $flat_versions[] = $micro['original_version']; - } - } - } - - // reverse the order, this makes it descending - $flat_versions = array_reverse($flat_versions); - - return $flat_versions; -} - -function parseVersion($version){ - $version_parts = []; - $raw_parts = []; - preg_match('#(?P\d+)\.(?P\d+).(?P\d+)[-]?(?PRC|alpha|beta|dev)?(?P[\d]?).*#ui', $version, $raw_parts); - $version_parts = [ - 'major' => $raw_parts['major'], - 'minor' => $raw_parts['minor'], - 'micro' => $raw_parts['micro'], - 'type' => strtolower($raw_parts['type']?$raw_parts['type']:'stable'), - 'number' => $raw_parts['number'], - 'original_version' => $version, - ]; - return $version_parts; -} diff --git a/src/Utils/Cache.php b/src/Utils/Cache.php new file mode 100644 index 00000000..2cf29a55 --- /dev/null +++ b/src/Utils/Cache.php @@ -0,0 +1,157 @@ +dir = $dir; + + // Create cache directory if it doesn't exist. + if (!file_exists($this->dir)) { + mkdir($this->dir, 0777, true); + chmod($this->dir, 0777); + } + + // Validate cache directory + if (!is_dir($this->dir)) { + throw new \Exception($this->dir.' is not a valid directory.'); + } + } + + /** + * Write data to cache file. + */ + public function set(string $key, $data, int $ttl = self::TTL): void + { + if (!$this->validateKey($key)) { + throw new Exception('Key name '.$key.' is invalid.'); + } + + $item = [time() + $ttl, serialize($data)]; + $this->pool[$key] = $data; + + $string = 'dir.'/'.$key.'.php', $string); + } + + /** + * Check if item has been cached and is available. + */ + public function has(string $key): bool + { + if (isset($this->pool[$key])) { + return true; + } + + $file = $this->dir.'/'.$key.'.php'; + + if (!is_file($file)) { + return false; + } + + $data = require $file; + + if (is_array($data) && isset($data[0]) && is_int($data[0]) && time() < $data[0]) { + return true; + } + + return false; + } + + /** + * Get data from the cache pool. + */ + public function get(string $key): ?array + { + if (isset($this->pool[$key])) { + return $this->pool[$key]; + } + + $file = $this->dir.'/'.$key.'.php'; + + if (is_file($file)) { + $data = require $file; + $this->pool[$key] = unserialize($data[1]); + + return $this->pool[$key]; + } + + return null; + } + + /** + * Wipes entire cache. + */ + public function clear(): bool + { + $success = true; + + $this->pool = []; + + foreach (new \DirectoryIterator($this->dir) as $fileInfo) { + if ($fileInfo->isDot()) { + continue; + } + + if (!unlink($fileInfo->getRealPath())) { + $success = false; + } + } + + return $success; + } + + /** + * Delete item from the cache. + */ + public function delete(string $key): bool + { + $success = true; + + unset($this->pool[$key]); + + $file = $this->dir.'/'.$key.'.php'; + + if (is_file($file) && !unlink($file)) { + $success = false; + } + + return $success; + } + + /** + * Validate key. + */ + private function validateKey(string $key): bool + { + return (bool) preg_match('/[a-z\_\-0-9]/i', $key); + } +} diff --git a/src/Utils/ComposerScripts.php b/src/Utils/ComposerScripts.php new file mode 100644 index 00000000..769856da --- /dev/null +++ b/src/Utils/ComposerScripts.php @@ -0,0 +1,52 @@ +isDevMode() && !file_exists($targetFile)) { + copy($sampleFile, $targetFile); + } + } + + /** + * Create application temporary and upload directories which are not tracked + * in Git. + */ + public static function createDirectories(Event $event) + { + if (!$event->isDevMode()) { + return; + } + + $vendorDir = $event->getComposer()->getConfig()->get('vendor-dir'); + + require_once $vendorDir.'/autoload.php'; + + if (!file_exists(self::$cacheDir)) { + mkdir(self::$cacheDir, 0777, true); + chmod(self::$cacheDir, 0777); + } + + if (!file_exists(self::$uploadsDir)) { + mkdir(self::$uploadsDir, 0777, true); + chmod(self::$uploadsDir, 0777); + } + } +} diff --git a/src/Utils/Versions/Client.php b/src/Utils/Versions/Client.php new file mode 100644 index 00000000..bf91a9c1 --- /dev/null +++ b/src/Utils/Versions/Client.php @@ -0,0 +1,43 @@ +devVersionsUrl); + + return json_decode($json, true); + } + + /** + * Fetch stable versions from remote URL. + */ + public function fetchStableVersions(): array + { + $json = file_get_contents($this->stableVersionsUrl); + + return json_decode($json, true); + } +} diff --git a/src/Utils/Versions/Generator.php b/src/Utils/Versions/Generator.php new file mode 100644 index 00000000..0631c460 --- /dev/null +++ b/src/Utils/Versions/Generator.php @@ -0,0 +1,185 @@ +5.4>5.3>master) + * - Between minor versions ordering is done by the micro version if available. + * First the QA releases: alpha/beta/rc, then stable, then nightly versions + * (Git, snaps). Snaps are more or less Windows snapshot builds. + * + * The result is cached for 1 hour into a temporary file. + */ +class Generator +{ + /** + * PHP API pages client. + * + * @var Client + */ + private $client; + + /** + * Cache service for storing fetched versions. + * + * @var Cache + */ + private $cache; + + /** + * Time after cache file is considered expired in seconds. + */ + private const TTL = 3600; + + /** + * Additional versions appended to the list of generated versions. + */ + private const APPENDICES = [ + 'Next Major Version', + 'Next Minor Version', + 'Irrelevant', + ]; + + /** + * Class constructor. + */ + public function __construct(Client $client, Cache $cache) + { + $this->client = $client; + $this->cache = $cache; + } + + /** + * Get a list of valid PHP versions. Versions are cached for efficiency. + */ + public function getVersions(): array + { + if (!$this->cache->has('versions')) { + $this->cache->set('versions', $this->generateVersions(), self::TTL); + } + + return $this->cache->get('versions'); + } + + /** + * Return fetched and processed versions. + */ + private function generateVersions(): array + { + $versions = array_merge($this->getDevVersions(), $this->getStableVersions()); + rsort($versions); + + // Get minor branches (PHP 7.2, PHP 7.3, etc) + $branches = []; + foreach ($versions as $version) { + $parts = $this->parseVersion($version); + $branch = $parts['major'].'.'.$parts['minor']; + $branches[$branch] = $branch; + } + + $sorted = []; + + // Add versions grouped by branches + foreach ($branches as $branch) { + foreach ($versions as $version) { + $parts = $this->parseVersion($version); + if ($parts['major'].'.'.$parts['minor'] === $branch) { + $sorted[] = $version; + } + } + + // Append Git and snaps for each branch + foreach ($this->getAffixes() as $item) { + $sorted[] = $branch.$item; + } + } + + // Append master branch to the versions list + foreach ($this->getAffixes() as $item) { + $sorted[] = 'master-'.$item; + } + + // Append human readable versions + $sorted = array_merge($sorted, self::APPENDICES); + + return $sorted; + } + + /** + * Get version affixes such as Git or snapshots. + */ + protected function getAffixes(): array + { + $date = date('Y-m-d'); + + return [ + 'Git-'.$date.' (Git)', + 'Git-'.$date.' (snap)', + ]; + } + + /** + * Get alpha, beta and RC versions. + */ + private function getDevVersions(): array + { + $versions = []; + + foreach ($this->client->fetchDevVersions() as $version) { + $parts = $this->parseVersion($version); + if ('dev' !== $parts['type']) { + $versions[] = $version; + } + } + + return $versions; + } + + /** + * Get stable versions. + */ + private function getStableVersions(): array + { + $versions = []; + + foreach ($this->client->fetchStableVersions() as $releases) { + foreach ($releases as $release) { + $versions[] = $release['version']; + } + } + + return $versions; + } + + /** + * Parse the versions data string and convert it to array. + */ + private function parseVersion(string $version): array + { + $matches = []; + preg_match('#(?P\d+)\.(?P\d+).(?P\d+)[-]?(?PRC|alpha|beta|dev)?(?P[\d]?).*#ui', $version, $matches); + $parts = [ + 'major' => $matches['major'], + 'minor' => $matches['minor'], + 'micro' => $matches['micro'], + 'type' => strtolower($matches['type'] ? $matches['type'] : 'stable'), + 'number' => $matches['number'], + 'original' => $version, + ]; + + return $parts; + } +} diff --git a/tests/Utils/CacheTest.php b/tests/Utils/CacheTest.php new file mode 100644 index 00000000..0e045ed5 --- /dev/null +++ b/tests/Utils/CacheTest.php @@ -0,0 +1,41 @@ +cache = new Cache($this->cacheDir); + $this->cache->clear(); + } + + public function tearDown() + { + $this->cache->clear(); + rmdir($this->cacheDir); + } + + public function testHas() + { + $this->assertFalse($this->cache->has('foo')); + + $this->cache->set('foo', [1, 2, 3]); + $this->assertTrue($this->cache->has('foo')); + } + + public function testDelete() + { + $this->cache->set('bar', [1, 2, 3]); + $this->assertFileExists($this->cacheDir.'/bar.php'); + + $this->cache->delete('bar'); + $this->assertFalse(file_exists($this->cacheDir.'/bar.php')); + } +} diff --git a/tests/Utils/Versions/ClientTest.php b/tests/Utils/Versions/ClientTest.php new file mode 100644 index 00000000..54f3ec0a --- /dev/null +++ b/tests/Utils/Versions/ClientTest.php @@ -0,0 +1,36 @@ +client = new Client(); + + $reflection = new \ReflectionClass($this->client); + + $devVersionsUrl = $reflection->getProperty('devVersionsUrl'); + $devVersionsUrl->setAccessible(true); + $devVersionsUrl->setValue($this->client, __DIR__.'/../../mock/responses/dev-body.txt'); + + $stableVersionsUrl = $reflection->getProperty('stableVersionsUrl'); + $stableVersionsUrl->setAccessible(true); + $stableVersionsUrl->setValue($this->client, __DIR__.'/../../mock/responses/stable-body.txt'); + } + + public function testFetchDevVersions() + { + $this->assertInternalType('array', $this->client->fetchDevVersions()); + } + + public function testFetchStableVersions() + { + $this->assertInternalType('array', $this->client->fetchStableVersions()); + } +} diff --git a/tests/Utils/Versions/GeneratorTest.php b/tests/Utils/Versions/GeneratorTest.php new file mode 100644 index 00000000..386a5b6f --- /dev/null +++ b/tests/Utils/Versions/GeneratorTest.php @@ -0,0 +1,70 @@ +cache = new Cache($this->cacheDir); + $this->cache->clear(); + + // The results returned by the client depend on the remote URLs so we + // mock the returned results. + $this->client = $this->getMockBuilder(Client::class) + ->setMethods(['fetchDevVersions', 'fetchStableVersions']) + ->getMock(); + + $this->client->expects($this->once()) + ->method('fetchDevVersions') + ->will($this->returnValue(json_decode(file_get_contents(__DIR__.'/../../mock/responses/dev-body.txt', true)))); + + $this->client->expects($this->once()) + ->method('fetchStableVersions') + ->will($this->returnValue(json_decode(file_get_contents(__DIR__.'/../../mock/responses/stable-body.txt'), true))); + + $this->generator = $this->getMockBuilder(Generator::class) + ->setConstructorArgs([$this->client, $this->cache]) + ->setMethods(['getAffixes']) + ->getMock(); + + // The extra versions are always date dependant so we mock it to include + // static date done on the tests day. + $date = '2018-12-26'; + $this->generator->expects($this->any()) + ->method('getAffixes') + ->will($this->returnValue(['Git-'.$date.' (snap)', 'Git-'.$date.' (Git)',])); + } + + public function tearDown() + { + $this->cache->clear(); + rmdir($this->cacheDir); + } + + public function testVersions() + { + $versions = $this->generator->getVersions(); + + $this->assertInternalType('array', $versions); + $this->assertGreaterThan(5, count($versions)); + + $fixture = require __DIR__.'/../../fixtures/versions/versions.php'; + $cached = require $this->cacheDir.'/versions.php'; + + $this->assertEquals($fixture[1], $cached[1]); + $this->assertContains('Next Major Version', $versions); + $this->assertContains('Irrelevant', $versions); + $this->assertContains('7.2.14RC1', $versions); + } +} diff --git a/tests/fixtures/versions/versions.php b/tests/fixtures/versions/versions.php new file mode 100644 index 00000000..6cae1956 --- /dev/null +++ b/tests/fixtures/versions/versions.php @@ -0,0 +1,4 @@ + 1545811329, + 1 => 'a:22:{i:0;s:8:"7.3.1RC1";i:1;s:5:"7.3.0";i:2;s:24:"7.3Git-2018-12-26 (snap)";i:3;s:23:"7.3Git-2018-12-26 (Git)";i:4;s:9:"7.2.14RC1";i:5;s:6:"7.2.13";i:6;s:24:"7.2Git-2018-12-26 (snap)";i:7;s:23:"7.2Git-2018-12-26 (Git)";i:8;s:6:"7.1.25";i:9;s:24:"7.1Git-2018-12-26 (snap)";i:10;s:23:"7.1Git-2018-12-26 (Git)";i:11;s:6:"7.0.33";i:12;s:24:"7.0Git-2018-12-26 (snap)";i:13;s:23:"7.0Git-2018-12-26 (Git)";i:14;s:6:"5.6.39";i:15;s:24:"5.6Git-2018-12-26 (snap)";i:16;s:23:"5.6Git-2018-12-26 (Git)";i:17;s:28:"master-Git-2018-12-26 (snap)";i:18;s:27:"master-Git-2018-12-26 (Git)";i:19;s:18:"Next Major Version";i:20;s:18:"Next Minor Version";i:21;s:10:"Irrelevant";}', +); diff --git a/tests/mock/responses/dev-body.txt b/tests/mock/responses/dev-body.txt new file mode 100644 index 00000000..77697a63 --- /dev/null +++ b/tests/mock/responses/dev-body.txt @@ -0,0 +1 @@ +["5.6.40-dev","7.0.33-dev","7.1.25-dev","7.2.14-dev","7.2.14RC1","7.3.1-dev","7.3.1RC1"] diff --git a/tests/mock/responses/stable-body.txt b/tests/mock/responses/stable-body.txt new file mode 100644 index 00000000..d4daa417 --- /dev/null +++ b/tests/mock/responses/stable-body.txt @@ -0,0 +1 @@ +{"5":{"5.6":{"announcement":true,"source":[{"filename":"php-5.6.39.tar.bz2","name":"PHP 5.6.39 (tar.bz2)","sha256":"b3db2345f50c010b01fe041b4e0f66c5aa28eb325135136f153e18da01583ad5","date":"06 Dec 2018"},{"filename":"php-5.6.39.tar.gz","name":"PHP 5.6.39 (tar.gz)","sha256":"127b122b7d6c7f3c211c0ffa554979370c3131196137404a51a391d8e2e9c7bb","date":"06 Dec 2018"},{"filename":"php-5.6.39.tar.xz","name":"PHP 5.6.39 (tar.xz)","sha256":"8147576001a832ff3d03cb2980caa2d6b584a10624f87ac459fcd3948c6e4a10","date":"06 Dec 2018"}],"version":"5.6.39"}},"7":{"7.0":{"announcement":true,"source":[{"filename":"php-7.0.33.tar.bz2","name":"PHP 7.0.33 (tar.bz2)","sha256":"4933ea74298a1ba046b0246fe3771415c84dfb878396201b56cb5333abe86f07","date":"06 Dec 2018"},{"filename":"php-7.0.33.tar.gz","name":"PHP 7.0.33 (tar.gz)","sha256":"d71a6ecb6b13dc53fed7532a7f8f949c4044806f067502f8fb6f9facbb40452a","date":"06 Dec 2018"},{"filename":"php-7.0.33.tar.xz","name":"PHP 7.0.33 (tar.xz)","sha256":"ab8c5be6e32b1f8d032909dedaaaa4bbb1a209e519abb01a52ce3914f9a13d96","date":"06 Dec 2018"}],"version":"7.0.33"},"7.1":{"announcement":true,"source":[{"filename":"php-7.1.25.tar.bz2","name":"PHP 7.1.25 (tar.bz2)","sha256":"002cdc880ac7cfaede2c389204d366108847db0f3ac72edf1ba95c0577f9aaac","date":"06 Dec 2018"},{"filename":"php-7.1.25.tar.gz","name":"PHP 7.1.25 (tar.gz)","sha256":"7dc40e202140e8b4fb3d992c15a68d98dc06b805e6b218497d260abbe51f5958","date":"06 Dec 2018"},{"filename":"php-7.1.25.tar.xz","name":"PHP 7.1.25 (tar.xz)","sha256":"0fd8dad1903cd0b2d615a1fe4209f99e53b7292403c8ffa1919c0f4dd1eada88","date":"06 Dec 2018"}],"version":"7.1.25"},"7.2":{"announcement":true,"source":[{"filename":"php-7.2.13.tar.bz2","name":"PHP 7.2.13 (tar.bz2)","sha256":"5b4a46fb76491bcd3eee1213773382e570f6ecf9b22d623b24e2822298b3e92d","date":"06 Dec 2018"},{"filename":"php-7.2.13.tar.gz","name":"PHP 7.2.13 (tar.gz)","sha256":"e563cee406b1ec96649c22ed2b35796cfe4e9aa9afa6eab6be4cf2fe5d724744","date":"06 Dec 2018"},{"filename":"php-7.2.13.tar.xz","name":"PHP 7.2.13 (tar.xz)","sha256":"14b0429abdb46b65c843e5882c9a8c46b31dfbf279c747293b8ab950c2644a4b","date":"06 Dec 2018"}],"version":"7.2.13"},"7.3":{"announcement":true,"source":[{"filename":"php-7.3.0.tar.bz2","name":"PHP 7.3.0 (tar.bz2)","sha256":"7a267daec9969a997c5c8028c350229646748e0fcc71e2f2dbb157ddcee87c67","date":"06 Dec 2018"},{"filename":"php-7.3.0.tar.gz","name":"PHP 7.3.0 (tar.gz)","sha256":"391bd0f91d9bdd01ab47ef9607bad8c65e35bc9bb098fb7777b2556e2c847b11","date":"06 Dec 2018"},{"filename":"php-7.3.0.tar.xz","name":"PHP 7.3.0 (tar.xz)","sha256":"7d195cad55af8b288c3919c67023a14ff870a73e3acc2165a6d17a4850a560b5","date":"06 Dec 2018"}],"version":"7.3.0"}}} diff --git a/www/report.php b/www/report.php index b5e4ebe0..9a7b61b5 100644 --- a/www/report.php +++ b/www/report.php @@ -2,9 +2,12 @@ use App\Repository\PackageRepository; use App\Repository\ReasonRepository; +use App\Utils\Cache; use App\Utils\Captcha; use App\Utils\PatchTracker; use App\Utils\Uploader; +use App\Utils\Versions\Client; +use App\Utils\Versions\Generator; // Obtain common includes require_once '../include/prepend.php'; @@ -22,7 +25,11 @@ // Authenticate bugs_authenticate($user, $pw, $logged_in, $user_flags); -require "{$ROOT_DIR}/include/php_versions.php"; +$versionsClient = new Client(); +$cacheDir = (defined('DEVBOX') && true === DEVBOX) ? __DIR__.'/../var/cache' : '/tmp'; +$cache = new Cache($cacheDir); +$versionsGenerator = new Generator($versionsClient, $cache); +$versions = $versionsGenerator->getVersions(); // captcha is not necessary if the user is logged in if (!$logged_in) { From e3c4b0ace366b36e0da2bcb7492e8ce643359022 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 23 Jan 2019 01:13:15 +0100 Subject: [PATCH 177/277] Replace deprecated Text_Diff PEAR package with Horde Text_Diff This patch replaces obsolete Text_Diff PEAR package with newer and still maintained Horde Text_Diff version 2.2.0 as suggested in the packages descriptions. Since bugs.php.net production is not ready for Composer installations yet, the Text_Diff package is bundled in the Git repository directly. Its classes are not compliant with PSR-4 yet so the classmap has been used until future PSR-4 migration and refactorings. --- README.md | 12 +- composer.json | 3 +- include/classes/bug_diff_renderer.php | 76 ---- include/prepend.php | 9 + src/Horde/Text/Diff.php | 233 ++++++++++ src/Horde/Text/Diff/Engine/Native.php | 425 ++++++++++++++++++ src/Horde/Text/Diff/Engine/Shell.php | 158 +++++++ src/Horde/Text/Diff/Engine/String.php | 247 ++++++++++ src/Horde/Text/Diff/Engine/Xdiff.php | 67 +++ src/Horde/Text/Diff/Exception.php | 17 + src/Horde/Text/Diff/Mapped.php | 61 +++ src/Horde/Text/Diff/Op/Add.php | 27 ++ src/Horde/Text/Diff/Op/Base.php | 31 ++ src/Horde/Text/Diff/Op/Change.php | 27 ++ src/Horde/Text/Diff/Op/Copy.php | 30 ++ src/Horde/Text/Diff/Op/Delete.php | 27 ++ src/Horde/Text/Diff/Renderer.php | 234 ++++++++++ src/Horde/Text/Diff/Renderer/Context.php | 68 +++ src/Horde/Text/Diff/Renderer/Inline.php | 193 ++++++++ src/Horde/Text/Diff/Renderer/Unified.php | 57 +++ .../Text/Diff/Renderer/Unified/Colored.php | 67 +++ src/Horde/Text/Diff/ThreeWay.php | 143 ++++++ src/Horde/Text/Diff/ThreeWay/BlockBuilder.php | 64 +++ src/Horde/Text/Diff/ThreeWay/Op/Base.php | 41 ++ src/Horde/Text/Diff/ThreeWay/Op/Copy.php | 29 ++ src/Utils/Diff.php | 77 ++++ www/patch-display.php | 10 +- 27 files changed, 2343 insertions(+), 90 deletions(-) delete mode 100644 include/classes/bug_diff_renderer.php create mode 100644 src/Horde/Text/Diff.php create mode 100644 src/Horde/Text/Diff/Engine/Native.php create mode 100644 src/Horde/Text/Diff/Engine/Shell.php create mode 100644 src/Horde/Text/Diff/Engine/String.php create mode 100644 src/Horde/Text/Diff/Engine/Xdiff.php create mode 100644 src/Horde/Text/Diff/Exception.php create mode 100644 src/Horde/Text/Diff/Mapped.php create mode 100644 src/Horde/Text/Diff/Op/Add.php create mode 100644 src/Horde/Text/Diff/Op/Base.php create mode 100644 src/Horde/Text/Diff/Op/Change.php create mode 100644 src/Horde/Text/Diff/Op/Copy.php create mode 100644 src/Horde/Text/Diff/Op/Delete.php create mode 100644 src/Horde/Text/Diff/Renderer.php create mode 100644 src/Horde/Text/Diff/Renderer/Context.php create mode 100644 src/Horde/Text/Diff/Renderer/Inline.php create mode 100644 src/Horde/Text/Diff/Renderer/Unified.php create mode 100644 src/Horde/Text/Diff/Renderer/Unified/Colored.php create mode 100644 src/Horde/Text/Diff/ThreeWay.php create mode 100644 src/Horde/Text/Diff/ThreeWay/BlockBuilder.php create mode 100644 src/Horde/Text/Diff/ThreeWay/Op/Base.php create mode 100644 src/Horde/Text/Diff/ThreeWay/Op/Copy.php create mode 100644 src/Utils/Diff.php diff --git a/README.md b/README.md index e24aca40..275f64d8 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,6 @@ This is a unified bug tracking system for PHP hosted online at composer install ``` -* Install required dependencies using PEAR: - -```bash -pear channel-update pear.php.net -pear install --alldeps Text_Diff -``` - * Configuration: Modify `local_config.php` according to your local development environment. @@ -45,14 +38,15 @@ Source code of this application is structured in the following directories: ├─ .git/ # Git configuration and source directory ├─ docs/ # Application documentation └─ include/ # Application helper functions and configuration - ├─ classes/ # PEAR class overrides ├─ prepend.php # Autoloader, DB connection, container, app initialization └─ ... └─ scripts/ # Command line development tools and scripts ├─ cron/ # Various systems scripts to run periodically on the server └─ ... ├─ sql/ # Database schema and fixtures - ├─ src/ # Application source code classes + └─ src/ # Application source code classes + ├─ Horde/ # https://www.horde.org/libraries/Horde_Text_Diff + └─ ... ├─ templates/ # Application templates ├─ tests/ # Application automated tests ├─ uploads/ # Uploaded patch files diff --git a/composer.json b/composer.json index cddad878..438518b0 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,8 @@ "autoload": { "psr-4": { "App\\": "src/" - } + }, + "classmap": ["src/Horde/"] }, "autoload-dev": { "psr-4": { diff --git a/include/classes/bug_diff_renderer.php b/include/classes/bug_diff_renderer.php deleted file mode 100644 index 0071109f..00000000 --- a/include/classes/bug_diff_renderer.php +++ /dev/null @@ -1,76 +0,0 @@ -diff = $d; - parent::__construct(); - } - - public function _blockHeader($xbeg, $xlen, $ybeg, $ylen) - { - $removed = $xlen - $ylen; - if ($removed > 0) { - return 'Line ' . $xbeg . ' (now ' . $ybeg . '), was ' . $xlen . ' lines, now ' . $ylen . ' lines'; - } - } - - public function _added($lines) - { - self::escapeHTML($lines); - - return ' ' . implode("\n ", $lines) . ''; - } - - public function _context($lines) - { - self::escapeHTML($lines); - - return "\n" . parent::_context($lines); - } - - public function _deleted($lines) - { - self::escapeHTML($lines); - - return ' ' . implode("\n ", $lines) . ''; - } - - public function _changed($orig, $final) - { - return $this->_deleted($orig) . "\n" . $this->_added($final); - } - - public function render($diff) - { - return parent::render($this->diff); - } - - protected static function escapeHTML(&$lines) - { - array_walk($lines, function(&$a, $b) - { - $a = htmlspecialchars($a); - }); - } -} diff --git a/include/prepend.php b/include/prepend.php index 5b94bf38..49400f95 100644 --- a/include/prepend.php +++ b/include/prepend.php @@ -15,6 +15,15 @@ $loader = new Autoloader(); $loader->addNamespace('App\\', __DIR__.'/../src/'); + + $loader->addClassmap('Horde_Text_Diff', __DIR__.'/../src/Horde/Text/Diff.php'); + $loader->addClassmap('Horde_Text_Diff_Engine_Native', __DIR__.'/../src/Horde/Text/Diff/Engine/Native.php'); + $loader->addClassmap('Horde_Text_Diff_Op_Add', __DIR__.'/../src/Horde/Text/Diff/Op/Add.php'); + $loader->addClassmap('Horde_Text_Diff_Op_Base', __DIR__.'/../src/Horde/Text/Diff/Op/Base.php'); + $loader->addClassmap('Horde_Text_Diff_Op_Change', __DIR__.'/../src/Horde/Text/Diff/Op/Change.php'); + $loader->addClassmap('Horde_Text_Diff_Op_Copy', __DIR__.'/../src/Horde/Text/Diff/Op/Copy.php'); + $loader->addClassmap('Horde_Text_Diff_Op_Delete', __DIR__.'/../src/Horde/Text/Diff/Op/Delete.php'); + $loader->addClassmap('Horde_Text_Diff_Renderer', __DIR__.'/../src/Horde/Text/Diff/Renderer.php'); } $site = 'php'; diff --git a/src/Horde/Text/Diff.php b/src/Horde/Text/Diff.php new file mode 100644 index 00000000..32ad0359 --- /dev/null +++ b/src/Horde/Text/Diff.php @@ -0,0 +1,233 @@ +, and is used/adapted with his permission. + * + * Copyright 2004 Geoffrey T. Dairiki + * Copyright 2004-2017 Horde LLC (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://www.horde.org/licenses/lgpl21. + * + * @package Text_Diff + * @author Geoffrey T. Dairiki + */ +class Horde_Text_Diff +{ + /** + * Array of changes. + * + * @var array + */ + protected $_edits; + + /** + * Computes diffs between sequences of strings. + * + * @param string $engine Name of the diffing engine to use. 'auto' + * will automatically select the best. + * @param array $params Parameters to pass to the diffing engine. + * Normally an array of two arrays, each + * containing the lines from a file. + */ + public function __construct($engine, $params) + { + if ($engine == 'auto') { + $engine = extension_loaded('xdiff') ? 'Xdiff' : 'Native'; + } else { + $engine = Horde_String::ucfirst(basename($engine)); + } + + $class = 'Horde_Text_Diff_Engine_' . $engine; + $diff_engine = new $class(); + + $this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params); + } + + /** + * Returns the array of differences. + */ + public function getDiff() + { + return $this->_edits; + } + + /** + * returns the number of new (added) lines in a given diff. + * + * @return integer The number of new lines + */ + public function countAddedLines() + { + $count = 0; + foreach ($this->_edits as $edit) { + if ($edit instanceof Horde_Text_Diff_Op_Add || + $edit instanceof Horde_Text_Diff_Op_Change) { + $count += $edit->nfinal(); + } + } + return $count; + } + + /** + * Returns the number of deleted (removed) lines in a given diff. + * + * @return integer The number of deleted lines + */ + public function countDeletedLines() + { + $count = 0; + foreach ($this->_edits as $edit) { + if ($edit instanceof Horde_Text_Diff_Op_Delete || + $edit instanceof Horde_Text_Diff_Op_Change) { + $count += $edit->norig(); + } + } + return $count; + } + + /** + * Computes a reversed diff. + * + * Example: + * + * $diff = new Horde_Text_Diff($lines1, $lines2); + * $rev = $diff->reverse(); + * + * + * @return Horde_Text_Diff A Diff object representing the inverse of the + * original diff. Note that we purposely don't return a + * reference here, since this essentially is a clone() + * method. + */ + public function reverse() + { + if (version_compare(zend_version(), '2', '>')) { + $rev = clone($this); + } else { + $rev = $this; + } + $rev->_edits = array(); + foreach ($this->_edits as $edit) { + $rev->_edits[] = $edit->reverse(); + } + return $rev; + } + + /** + * Checks for an empty diff. + * + * @return boolean True if two sequences were identical. + */ + public function isEmpty() + { + foreach ($this->_edits as $edit) { + if (!($edit instanceof Horde_Text_Diff_Op_Copy)) { + return false; + } + } + return true; + } + + /** + * Computes the length of the Longest Common Subsequence (LCS). + * + * This is mostly for diagnostic purposes. + * + * @return integer The length of the LCS. + */ + public function lcs() + { + $lcs = 0; + foreach ($this->_edits as $edit) { + if ($edit instanceof Horde_Text_Diff_Op_Copy) { + $lcs += count($edit->orig); + } + } + return $lcs; + } + + /** + * Gets the original set of lines. + * + * This reconstructs the $from_lines parameter passed to the constructor. + * + * @return array The original sequence of strings. + */ + public function getOriginal() + { + $lines = array(); + foreach ($this->_edits as $edit) { + if ($edit->orig) { + array_splice($lines, count($lines), 0, $edit->orig); + } + } + return $lines; + } + + /** + * Gets the final set of lines. + * + * This reconstructs the $to_lines parameter passed to the constructor. + * + * @return array The sequence of strings. + */ + public function getFinal() + { + $lines = array(); + foreach ($this->_edits as $edit) { + if ($edit->final) { + array_splice($lines, count($lines), 0, $edit->final); + } + } + return $lines; + } + + /** + * Removes trailing newlines from a line of text. This is meant to be used + * with array_walk(). + * + * @param string $line The line to trim. + * @param integer $key The index of the line in the array. Not used. + */ + public static function trimNewlines(&$line, $key) + { + $line = str_replace(array("\n", "\r"), '', $line); + } + + /** + * Checks a diff for validity. + * + * This is here only for debugging purposes. + */ + protected function _check($from_lines, $to_lines) + { + if (serialize($from_lines) != serialize($this->getOriginal())) { + trigger_error("Reconstructed original doesn't match", E_USER_ERROR); + } + if (serialize($to_lines) != serialize($this->getFinal())) { + trigger_error("Reconstructed final doesn't match", E_USER_ERROR); + } + + $rev = $this->reverse(); + if (serialize($to_lines) != serialize($rev->getOriginal())) { + trigger_error("Reversed original doesn't match", E_USER_ERROR); + } + if (serialize($from_lines) != serialize($rev->getFinal())) { + trigger_error("Reversed final doesn't match", E_USER_ERROR); + } + + $prevtype = null; + foreach ($this->_edits as $edit) { + if ($prevtype == get_class($edit)) { + trigger_error("Edit sequence is non-optimal", E_USER_ERROR); + } + $prevtype = get_class($edit); + } + + return true; + } +} diff --git a/src/Horde/Text/Diff/Engine/Native.php b/src/Horde/Text/Diff/Engine/Native.php new file mode 100644 index 00000000..060c380e --- /dev/null +++ b/src/Horde/Text/Diff/Engine/Native.php @@ -0,0 +1,425 @@ + 2, and some optimizations) are from + * Geoffrey T. Dairiki . The original PHP version of this + * code was written by him, and is used/adapted with his permission. + * + * Copyright 2004-2017 Horde LLC (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://www.horde.org/licenses/lgpl21. + * + * @author Geoffrey T. Dairiki + * @package Text_Diff + */ +class Horde_Text_Diff_Engine_Native +{ + public function diff($from_lines, $to_lines) + { + array_walk($from_lines, array('Horde_Text_Diff', 'trimNewlines')); + array_walk($to_lines, array('Horde_Text_Diff', 'trimNewlines')); + + $n_from = count($from_lines); + $n_to = count($to_lines); + + $this->xchanged = $this->ychanged = array(); + $this->xv = $this->yv = array(); + $this->xind = $this->yind = array(); + unset($this->seq); + unset($this->in_seq); + unset($this->lcs); + + // Skip leading common lines. + for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) { + if ($from_lines[$skip] !== $to_lines[$skip]) { + break; + } + $this->xchanged[$skip] = $this->ychanged[$skip] = false; + } + + // Skip trailing common lines. + $xi = $n_from; $yi = $n_to; + for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) { + if ($from_lines[$xi] !== $to_lines[$yi]) { + break; + } + $this->xchanged[$xi] = $this->ychanged[$yi] = false; + } + + // Ignore lines which do not exist in both files. + for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { + $xhash[$from_lines[$xi]] = 1; + } + for ($yi = $skip; $yi < $n_to - $endskip; $yi++) { + $line = $to_lines[$yi]; + if (($this->ychanged[$yi] = empty($xhash[$line]))) { + continue; + } + $yhash[$line] = 1; + $this->yv[] = $line; + $this->yind[] = $yi; + } + for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { + $line = $from_lines[$xi]; + if (($this->xchanged[$xi] = empty($yhash[$line]))) { + continue; + } + $this->xv[] = $line; + $this->xind[] = $xi; + } + + // Find the LCS. + $this->_compareseq(0, count($this->xv), 0, count($this->yv)); + + // Merge edits when possible. + $this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged); + $this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged); + + // Compute the edit operations. + $edits = array(); + $xi = $yi = 0; + while ($xi < $n_from || $yi < $n_to) { + assert($yi < $n_to || $this->xchanged[$xi]); + assert($xi < $n_from || $this->ychanged[$yi]); + + // Skip matching "snake". + $copy = array(); + while ($xi < $n_from && $yi < $n_to + && !$this->xchanged[$xi] && !$this->ychanged[$yi]) { + $copy[] = $from_lines[$xi++]; + ++$yi; + } + if ($copy) { + $edits[] = new Horde_Text_Diff_Op_Copy($copy); + } + + // Find deletes & adds. + $delete = array(); + while ($xi < $n_from && $this->xchanged[$xi]) { + $delete[] = $from_lines[$xi++]; + } + + $add = array(); + while ($yi < $n_to && $this->ychanged[$yi]) { + $add[] = $to_lines[$yi++]; + } + + if ($delete && $add) { + $edits[] = new Horde_Text_Diff_Op_Change($delete, $add); + } elseif ($delete) { + $edits[] = new Horde_Text_Diff_Op_Delete($delete); + } elseif ($add) { + $edits[] = new Horde_Text_Diff_Op_Add($add); + } + } + + return $edits; + } + + /** + * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF, + * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized + * segments. + * + * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an array of + * NCHUNKS+1 (X, Y) indexes giving the diving points between sub + * sequences. The first sub-sequence is contained in (X0, X1), (Y0, Y1), + * the second in (X1, X2), (Y1, Y2) and so on. Note that (X0, Y0) == + * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM). + * + * This public function assumes that the first lines of the specified portions of + * the two files do not match, and likewise that the last lines do not + * match. The caller must trim matching lines from the beginning and end + * of the portions it is going to specify. + */ + protected function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) + { + $flip = false; + + if ($xlim - $xoff > $ylim - $yoff) { + /* Things seems faster (I'm not sure I understand why) when the + * shortest sequence is in X. */ + $flip = true; + list ($xoff, $xlim, $yoff, $ylim) + = array($yoff, $ylim, $xoff, $xlim); + } + + if ($flip) { + for ($i = $ylim - 1; $i >= $yoff; $i--) { + $ymatches[$this->xv[$i]][] = $i; + } + } else { + for ($i = $ylim - 1; $i >= $yoff; $i--) { + $ymatches[$this->yv[$i]][] = $i; + } + } + + $this->lcs = 0; + $this->seq[0]= $yoff - 1; + $this->in_seq = array(); + $ymids[0] = array(); + + $numer = $xlim - $xoff + $nchunks - 1; + $x = $xoff; + for ($chunk = 0; $chunk < $nchunks; $chunk++) { + if ($chunk > 0) { + for ($i = 0; $i <= $this->lcs; $i++) { + $ymids[$i][$chunk - 1] = $this->seq[$i]; + } + } + + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $chunk) / $nchunks); + for (; $x < $x1; $x++) { + $line = $flip ? $this->yv[$x] : $this->xv[$x]; + if (empty($ymatches[$line])) { + continue; + } + $matches = $ymatches[$line]; + foreach ($matches as $y) { + if (empty($this->in_seq[$y])) { + $k = $this->_lcsPos($y); + assert($k > 0); + $ymids[$k] = $ymids[$k - 1]; + break; + } elseif ($y > $this->seq[$k - 1]) { + assert($y <= $this->seq[$k]); + $this->in_seq[$this->seq[$k]] = false; + $this->seq[$k] = $y; + $this->in_seq[$y] = 1; + } + } + } + } + + $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff); + $ymid = $ymids[$this->lcs]; + for ($n = 0; $n < $nchunks - 1; $n++) { + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks); + $y1 = $ymid[$n] + 1; + $seps[] = $flip ? array($y1, $x1) : array($x1, $y1); + } + $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim); + + return array($this->lcs, $seps); + } + + protected function _lcsPos($ypos) + { + $end = $this->lcs; + if ($end == 0 || $ypos > $this->seq[$end]) { + $this->seq[++$this->lcs] = $ypos; + $this->in_seq[$ypos] = 1; + return $this->lcs; + } + + $beg = 1; + while ($beg < $end) { + $mid = (int)(($beg + $end) / 2); + if ($ypos > $this->seq[$mid]) { + $beg = $mid + 1; + } else { + $end = $mid; + } + } + + assert($ypos != $this->seq[$end]); + + $this->in_seq[$this->seq[$end]] = false; + $this->seq[$end] = $ypos; + $this->in_seq[$ypos] = 1; + return $end; + } + + /** + * Finds LCS of two sequences. + * + * The results are recorded in the vectors $this->{x,y}changed[], by + * storing a 1 in the element for each line that is an insertion or + * deletion (ie. is not in the LCS). + * + * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1. + * + * Note that XLIM, YLIM are exclusive bounds. All line numbers are + * origin-0 and discarded lines are not counted. + */ + protected function _compareseq ($xoff, $xlim, $yoff, $ylim) + { + /* Slide down the bottom initial diagonal. */ + while ($xoff < $xlim && $yoff < $ylim + && $this->xv[$xoff] == $this->yv[$yoff]) { + ++$xoff; + ++$yoff; + } + + /* Slide up the top initial diagonal. */ + while ($xlim > $xoff && $ylim > $yoff + && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) { + --$xlim; + --$ylim; + } + + if ($xoff == $xlim || $yoff == $ylim) { + $lcs = 0; + } else { + /* This is ad hoc but seems to work well. $nchunks = + * sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); $nchunks = + * max(2,min(8,(int)$nchunks)); */ + $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1; + list($lcs, $seps) + = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks); + } + + if ($lcs == 0) { + /* X and Y sequences have no common subsequence: mark all + * changed. */ + while ($yoff < $ylim) { + $this->ychanged[$this->yind[$yoff++]] = 1; + } + while ($xoff < $xlim) { + $this->xchanged[$this->xind[$xoff++]] = 1; + } + } else { + /* Use the partitions to split this problem into subproblems. */ + reset($seps); + $pt1 = $seps[0]; + while ($pt2 = next($seps)) { + $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]); + $pt1 = $pt2; + } + } + } + + /** + * Adjusts inserts/deletes of identical lines to join changes as much as + * possible. + * + * We do something when a run of changed lines include a line at one end + * and has an excluded, identical line at the other. We are free to + * choose which identical line is included. `compareseq' usually chooses + * the one at the beginning, but usually it is cleaner to consider the + * following identical line to be the "change". + * + * This is extracted verbatim from analyze.c (GNU diffutils-2.7). + */ + protected function _shiftBoundaries($lines, &$changed, $other_changed) + { + $i = 0; + $j = 0; + + assert(count($lines) == count($changed)); + $len = count($lines); + $other_len = count($other_changed); + + while (1) { + /* Scan forward to find the beginning of another run of + * changes. Also keep track of the corresponding point in the + * other file. + * + * Throughout this code, $i and $j are adjusted together so that + * the first $i elements of $changed and the first $j elements of + * $other_changed both contain the same number of zeros (unchanged + * lines). + * + * Furthermore, $j is always kept so that $j == $other_len or + * $other_changed[$j] == false. */ + while ($j < $other_len && $other_changed[$j]) { + $j++; + } + + while ($i < $len && ! $changed[$i]) { + assert($j < $other_len && ! $other_changed[$j]); + $i++; $j++; + while ($j < $other_len && $other_changed[$j]) { + $j++; + } + } + + if ($i == $len) { + break; + } + + $start = $i; + + /* Find the end of this run of changes. */ + while (++$i < $len && $changed[$i]) { + continue; + } + + do { + /* Record the length of this run of changes, so that we can + * later determine whether the run has grown. */ + $runlength = $i - $start; + + /* Move the changed region back, so long as the previous + * unchanged line matches the last changed one. This merges + * with previous changed regions. */ + while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) { + $changed[--$start] = 1; + $changed[--$i] = false; + while ($start > 0 && $changed[$start - 1]) { + $start--; + } + assert($j > 0); + while ($other_changed[--$j]) { + continue; + } + assert($j >= 0 && !$other_changed[$j]); + } + + /* Set CORRESPONDING to the end of the changed run, at the + * last point where it corresponds to a changed run in the + * other file. CORRESPONDING == LEN means no such point has + * been found. */ + $corresponding = $j < $other_len ? $i : $len; + + /* Move the changed region forward, so long as the first + * changed line matches the following unchanged one. This + * merges with following changed regions. Do this second, so + * that if there are no merges, the changed region is moved + * forward as far as possible. */ + while ($i < $len && $lines[$start] == $lines[$i]) { + $changed[$start++] = false; + $changed[$i++] = 1; + while ($i < $len && $changed[$i]) { + $i++; + } + + assert($j < $other_len && ! $other_changed[$j]); + $j++; + if ($j < $other_len && $other_changed[$j]) { + $corresponding = $i; + while ($j < $other_len && $other_changed[$j]) { + $j++; + } + } + } + } while ($runlength != $i - $start); + + /* If possible, move the fully-merged run of changes back to a + * corresponding run in the other file. */ + while ($corresponding < $i) { + $changed[--$start] = 1; + $changed[--$i] = 0; + assert($j > 0); + while ($other_changed[--$j]) { + continue; + } + assert($j >= 0 && !$other_changed[$j]); + } + } + } +} diff --git a/src/Horde/Text/Diff/Engine/Shell.php b/src/Horde/Text/Diff/Engine/Shell.php new file mode 100644 index 00000000..79b467c9 --- /dev/null +++ b/src/Horde/Text/Diff/Engine/Shell.php @@ -0,0 +1,158 @@ + + * @package Text_Diff + */ +class Horde_Text_Diff_Engine_Shell +{ + /** + * Path to the diff executable + * + * @var string + */ + protected $_diffCommand = 'diff'; + + /** + * Returns the array of differences. + * + * @param array $from_lines lines of text from old file + * @param array $to_lines lines of text from new file + * + * @return array all changes made (array with Horde_Text_Diff_Op_* objects) + */ + public function diff($from_lines, $to_lines) + { + array_walk($from_lines, array('Horde_Text_Diff', 'trimNewlines')); + array_walk($to_lines, array('Horde_Text_Diff', 'trimNewlines')); + + // Execute gnu diff or similar to get a standard diff file. + $from_file = Horde_Util::getTempFile('Horde_Text_Diff'); + $to_file = Horde_Util::getTempFile('Horde_Text_Diff'); + $fp = fopen($from_file, 'w'); + fwrite($fp, implode("\n", $from_lines)); + fclose($fp); + $fp = fopen($to_file, 'w'); + fwrite($fp, implode("\n", $to_lines)); + fclose($fp); + $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file); + unlink($from_file); + unlink($to_file); + + if (is_null($diff)) { + // No changes were made + return array(new Horde_Text_Diff_Op_Copy($from_lines)); + } + + $from_line_no = 1; + $to_line_no = 1; + $edits = array(); + + // Get changed lines by parsing something like: + // 0a1,2 + // 1,2c4,6 + // 1,5d6 + preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff, + $matches, PREG_SET_ORDER); + + foreach ($matches as $match) { + if (!isset($match[5])) { + // This paren is not set every time (see regex). + $match[5] = false; + } + + if ($match[3] == 'a') { + $from_line_no--; + } + + if ($match[3] == 'd') { + $to_line_no--; + } + + if ($from_line_no < $match[1] || $to_line_no < $match[4]) { + // copied lines + assert($match[1] - $from_line_no == $match[4] - $to_line_no); + $edits[] = + new Horde_Text_Diff_Op_Copy( + $this->_getLines($from_lines, $from_line_no, $match[1] - 1), + $this->_getLines($to_lines, $to_line_no, $match[4] - 1)); + } + + switch ($match[3]) { + case 'd': + // deleted lines + $edits[] = + new Horde_Text_Diff_Op_Delete( + $this->_getLines($from_lines, $from_line_no, $match[2])); + $to_line_no++; + break; + + case 'c': + // changed lines + $edits[] = + new Horde_Text_Diff_Op_Change( + $this->_getLines($from_lines, $from_line_no, $match[2]), + $this->_getLines($to_lines, $to_line_no, $match[5])); + break; + + case 'a': + // added lines + $edits[] = + new Horde_Text_Diff_Op_Add( + $this->_getLines($to_lines, $to_line_no, $match[5])); + $from_line_no++; + break; + } + } + + if (!empty($from_lines)) { + // Some lines might still be pending. Add them as copied + $edits[] = + new Horde_Text_Diff_Op_Copy( + $this->_getLines($from_lines, $from_line_no, + $from_line_no + count($from_lines) - 1), + $this->_getLines($to_lines, $to_line_no, + $to_line_no + count($to_lines) - 1)); + } + + return $edits; + } + + /** + * Get lines from either the old or new text + * + * @access private + * + * @param array &$text_lines Either $from_lines or $to_lines + * @param int &$line_no Current line number + * @param int $end Optional end line, when we want to chop more + * than one line. + * + * @return array The chopped lines + */ + protected function _getLines(&$text_lines, &$line_no, $end = false) + { + if (!empty($end)) { + $lines = array(); + // We can shift even more + while ($line_no <= $end) { + $lines[] = array_shift($text_lines); + $line_no++; + } + } else { + $lines = array(array_shift($text_lines)); + $line_no++; + } + + return $lines; + } +} diff --git a/src/Horde/Text/Diff/Engine/String.php b/src/Horde/Text/Diff/Engine/String.php new file mode 100644 index 00000000..28d16e73 --- /dev/null +++ b/src/Horde/Text/Diff/Engine/String.php @@ -0,0 +1,247 @@ + + * $patch = file_get_contents('example.patch'); + * $diff = new Horde_Text_Diff('string', array($patch)); + * $renderer = new Horde_Text_Diff_Renderer_inline(); + * echo $renderer->render($diff); + *
    + * + * Copyright 2005 Örjan Persson + * Copyright 2005-2017 Horde LLC (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://www.horde.org/licenses/lgpl21. + * + * @author Örjan Persson + * @package Text_Diff + */ +class Horde_Text_Diff_Engine_String +{ + /** + * Parses a unified or context diff. + * + * First param contains the whole diff and the second can be used to force + * a specific diff type. If the second parameter is 'autodetect', the + * diff will be examined to find out which type of diff this is. + * + * @param string $diff The diff content. + * @param string $mode The diff mode of the content in $diff. One of + * 'context', 'unified', or 'autodetect'. + * + * @return array List of all diff operations. + * @throws Horde_Text_Diff_Exception + */ + public function diff($diff, $mode = 'autodetect') + { + // Detect line breaks. + $lnbr = "\n"; + if (strpos($diff, "\r\n") !== false) { + $lnbr = "\r\n"; + } elseif (strpos($diff, "\r") !== false) { + $lnbr = "\r"; + } + + // Make sure we have a line break at the EOF. + if (substr($diff, -strlen($lnbr)) != $lnbr) { + $diff .= $lnbr; + } + + if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') { + throw new Horde_Text_Diff_Exception('Type of diff is unsupported'); + } + + if ($mode == 'autodetect') { + $context = strpos($diff, '***'); + $unified = strpos($diff, '---'); + if ($context === $unified) { + throw new Horde_Text_Diff_Exception('Type of diff could not be detected'); + } elseif ($context === false || $unified === false) { + $mode = $context !== false ? 'context' : 'unified'; + } else { + $mode = $context < $unified ? 'context' : 'unified'; + } + } + + // Split by new line and remove the diff header, if there is one. + $diff = explode($lnbr, $diff); + if (($mode == 'context' && strpos($diff[0], '***') === 0) || + ($mode == 'unified' && strpos($diff[0], '---') === 0)) { + array_shift($diff); + array_shift($diff); + } + + if ($mode == 'context') { + return $this->parseContextDiff($diff); + } else { + return $this->parseUnifiedDiff($diff); + } + } + + /** + * Parses an array containing the unified diff. + * + * @param array $diff Array of lines. + * + * @return array List of all diff operations. + */ + public function parseUnifiedDiff($diff) + { + $edits = array(); + $end = count($diff) - 1; + for ($i = 0; $i < $end;) { + $diff1 = array(); + switch (substr($diff[$i], 0, 1)) { + case ' ': + do { + $diff1[] = substr($diff[$i], 1); + } while (++$i < $end && substr($diff[$i], 0, 1) == ' '); + $edits[] = new Horde_Text_Diff_Op_Copy($diff1); + break; + + case '+': + // get all new lines + do { + $diff1[] = substr($diff[$i], 1); + } while (++$i < $end && substr($diff[$i], 0, 1) == '+'); + $edits[] = new Horde_Text_Diff_Op_Add($diff1); + break; + + case '-': + // get changed or removed lines + $diff2 = array(); + do { + $diff1[] = substr($diff[$i], 1); + } while (++$i < $end && substr($diff[$i], 0, 1) == '-'); + + while ($i < $end && substr($diff[$i], 0, 1) == '+') { + $diff2[] = substr($diff[$i++], 1); + } + if (count($diff2) == 0) { + $edits[] = new Horde_Text_Diff_Op_Delete($diff1); + } else { + $edits[] = new Horde_Text_Diff_Op_Change($diff1, $diff2); + } + break; + + default: + $i++; + break; + } + } + + return $edits; + } + + /** + * Parses an array containing the context diff. + * + * @param array $diff Array of lines. + * + * @return array List of all diff operations. + */ + public function parseContextDiff(&$diff) + { + $edits = array(); + $i = $max_i = $j = $max_j = 0; + $end = count($diff) - 1; + while ($i < $end && $j < $end) { + while ($i >= $max_i && $j >= $max_j) { + // Find the boundaries of the diff output of the two files + for ($i = $j; + $i < $end && substr($diff[$i], 0, 3) == '***'; + $i++); + for ($max_i = $i; + $max_i < $end && substr($diff[$max_i], 0, 3) != '---'; + $max_i++); + for ($j = $max_i; + $j < $end && substr($diff[$j], 0, 3) == '---'; + $j++); + for ($max_j = $j; + $max_j < $end && substr($diff[$max_j], 0, 3) != '***'; + $max_j++); + } + + // find what hasn't been changed + $array = array(); + while ($i < $max_i && + $j < $max_j && + strcmp($diff[$i], $diff[$j]) == 0) { + $array[] = substr($diff[$i], 2); + $i++; + $j++; + } + + while ($i < $max_i && ($max_j-$j) <= 1) { + if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') { + break; + } + $array[] = substr($diff[$i++], 2); + } + + while ($j < $max_j && ($max_i-$i) <= 1) { + if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') { + break; + } + $array[] = substr($diff[$j++], 2); + } + if (count($array) > 0) { + $edits[] = new Horde_Text_Diff_Op_Copy($array); + } + + if ($i < $max_i) { + $diff1 = array(); + switch (substr($diff[$i], 0, 1)) { + case '!': + $diff2 = array(); + do { + $diff1[] = substr($diff[$i], 2); + if ($j < $max_j && substr($diff[$j], 0, 1) == '!') { + $diff2[] = substr($diff[$j++], 2); + } + } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!'); + $edits[] = new Horde_Text_Diff_Op_Change($diff1, $diff2); + break; + + case '+': + do { + $diff1[] = substr($diff[$i], 2); + } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+'); + $edits[] = new Horde_Text_Diff_Op_Add($diff1); + break; + + case '-': + do { + $diff1[] = substr($diff[$i], 2); + } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-'); + $edits[] = new Horde_Text_Diff_Op_Delete($diff1); + break; + } + } + + if ($j < $max_j) { + $diff2 = array(); + switch (substr($diff[$j], 0, 1)) { + case '+': + do { + $diff2[] = substr($diff[$j++], 2); + } while ($j < $max_j && substr($diff[$j], 0, 1) == '+'); + $edits[] = new Horde_Text_Diff_Op_Add($diff2); + break; + + case '-': + do { + $diff2[] = substr($diff[$j++], 2); + } while ($j < $max_j && substr($diff[$j], 0, 1) == '-'); + $edits[] = new Horde_Text_Diff_Op_Delete($diff2); + break; + } + } + } + + return $edits; + } +} diff --git a/src/Horde/Text/Diff/Engine/Xdiff.php b/src/Horde/Text/Diff/Engine/Xdiff.php new file mode 100644 index 00000000..45bb41a6 --- /dev/null +++ b/src/Horde/Text/Diff/Engine/Xdiff.php @@ -0,0 +1,67 @@ + + * @package Text_Diff + */ +class Horde_Text_Diff_Engine_Xdiff +{ + /** + */ + public function diff($from_lines, $to_lines) + { + if (!extension_loaded('xdiff')) { + throw new Horde_Text_Diff_Exception('The xdiff extension is required for this diff engine'); + } + + array_walk($from_lines, array('Horde_Text_Diff', 'trimNewlines')); + array_walk($to_lines, array('Horde_Text_Diff', 'trimNewlines')); + + /* Convert the two input arrays into strings for xdiff processing. */ + $from_string = implode("\n", $from_lines); + $to_string = implode("\n", $to_lines); + + /* Diff the two strings and convert the result to an array. */ + $diff = xdiff_string_diff($from_string, $to_string, count($to_lines)); + $diff = explode("\n", $diff); + + /* Walk through the diff one line at a time. We build the $edits + * array of diff operations by reading the first character of the + * xdiff output (which is in the "unified diff" format). + * + * Note that we don't have enough information to detect "changed" + * lines using this approach, so we can't add Horde_Text_Diff_Op_Changed + * instances to the $edits array. The result is still perfectly + * valid, albeit a little less descriptive and efficient. */ + $edits = array(); + foreach ($diff as $line) { + if (!strlen($line)) { + continue; + } + switch ($line[0]) { + case ' ': + $edits[] = new Horde_Text_Diff_Op_Copy(array(substr($line, 1))); + break; + + case '+': + $edits[] = new Horde_Text_Diff_Op_Add(array(substr($line, 1))); + break; + + case '-': + $edits[] = new Horde_Text_Diff_Op_Delete(array(substr($line, 1))); + break; + } + } + + return $edits; + } +} diff --git a/src/Horde/Text/Diff/Exception.php b/src/Horde/Text/Diff/Exception.php new file mode 100644 index 00000000..4e514ff1 --- /dev/null +++ b/src/Horde/Text/Diff/Exception.php @@ -0,0 +1,17 @@ + + * @category Horde + * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 + * @package Text_Diff + */ +class Horde_Text_Diff_Exception extends Horde_Exception_Wrapped +{ +} diff --git a/src/Horde/Text/Diff/Mapped.php b/src/Horde/Text/Diff/Mapped.php new file mode 100644 index 00000000..4815d9fd --- /dev/null +++ b/src/Horde/Text/Diff/Mapped.php @@ -0,0 +1,61 @@ + + * @category Horde + * @license http://www.horde.org/licenses/lgpl21 LGPL-2.1 + * @package Text_Diff + */ + +/** + * This can be used to compute things like case-insensitve diffs, or diffs + * which ignore changes in white-space. + * + * @author Geoffrey T. Dairiki + * @category Horde + * @copyright 2007-2017 Horde LLC + * @license http://www.horde.org/licenses/lgpl21 LGPL-2.1 + * @package Text_Diff + */ +class Horde_Text_Diff_Mapped extends Horde_Text_Diff +{ + /** + * Computes a diff between sequences of strings. + * + * @param string $engine Name of the diffing engine to use. 'auto' will + * automatically select the best. + * @param array $params Parameters to pass to the diffing engine: + * - Two arrays, each containing the lines from a + * file. + * - Two arrays with the same size as the first + * parameters. The elements are what is actually + * compared when computing the diff. + */ + public function __construct($engine, $params) + { + list($from_lines, $to_lines, $mapped_from_lines, $mapped_to_lines) = $params; + assert(count($from_lines) == count($mapped_from_lines)); + assert(count($to_lines) == count($mapped_to_lines)); + + parent::__construct($engine, array($mapped_from_lines, $mapped_to_lines)); + + $xi = $yi = 0; + for ($i = 0; $i < count($this->_edits); $i++) { + $orig = &$this->_edits[$i]->orig; + if (is_array($orig)) { + $orig = array_slice($from_lines, $xi, count($orig)); + $xi += count($orig); + } + + $final = &$this->_edits[$i]->final; + if (is_array($final)) { + $final = array_slice($to_lines, $yi, count($final)); + $yi += count($final); + } + } + } +} diff --git a/src/Horde/Text/Diff/Op/Add.php b/src/Horde/Text/Diff/Op/Add.php new file mode 100644 index 00000000..20059436 --- /dev/null +++ b/src/Horde/Text/Diff/Op/Add.php @@ -0,0 +1,27 @@ +, and is used/adapted with his permission. + * + * Copyright 2004 Geoffrey T. Dairiki + * Copyright 2004-2017 Horde LLC (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://www.horde.org/licenses/lgpl21. + * + * @package Text_Diff + * @author Geoffrey T. Dairiki + */ +class Horde_Text_Diff_Op_Add extends Horde_Text_Diff_Op_Base +{ + public function __construct($lines) + { + $this->final = $lines; + $this->orig = false; + } + + public function reverse() + { + return new Horde_Text_Diff_Op_Delete($this->final); + } +} diff --git a/src/Horde/Text/Diff/Op/Base.php b/src/Horde/Text/Diff/Op/Base.php new file mode 100644 index 00000000..42b264fc --- /dev/null +++ b/src/Horde/Text/Diff/Op/Base.php @@ -0,0 +1,31 @@ +, and is used/adapted with his permission. + * + * Copyright 2004 Geoffrey T. Dairiki + * Copyright 2004-2017 Horde LLC (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://www.horde.org/licenses/lgpl21. + * + * @package Text_Diff + * @author Geoffrey T. Dairiki + */ +abstract class Horde_Text_Diff_Op_Base +{ + public $orig; + public $final; + + abstract public function reverse(); + + public function norig() + { + return $this->orig ? count($this->orig) : 0; + } + + public function nfinal() + { + return $this->final ? count($this->final) : 0; + } +} diff --git a/src/Horde/Text/Diff/Op/Change.php b/src/Horde/Text/Diff/Op/Change.php new file mode 100644 index 00000000..5ce341cd --- /dev/null +++ b/src/Horde/Text/Diff/Op/Change.php @@ -0,0 +1,27 @@ +, and is used/adapted with his permission. + * + * Copyright 2004 Geoffrey T. Dairiki + * Copyright 2004-2017 Horde LLC (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://www.horde.org/licenses/lgpl21. + * + * @package Text_Diff + * @author Geoffrey T. Dairiki + */ +class Horde_Text_Diff_Op_Change extends Horde_Text_Diff_Op_Base +{ + public function __construct($orig, $final) + { + $this->orig = $orig; + $this->final = $final; + } + + public function reverse() + { + return new Horde_Text_Diff_Op_Change($this->final, $this->orig); + } +} diff --git a/src/Horde/Text/Diff/Op/Copy.php b/src/Horde/Text/Diff/Op/Copy.php new file mode 100644 index 00000000..503d0ce1 --- /dev/null +++ b/src/Horde/Text/Diff/Op/Copy.php @@ -0,0 +1,30 @@ +, and is used/adapted with his permission. + * + * Copyright 2004 Geoffrey T. Dairiki + * Copyright 2004-2017 Horde LLC (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://www.horde.org/licenses/lgpl21. + * + * @package Text_Diff + * @author Geoffrey T. Dairiki + */ +class Horde_Text_Diff_Op_Copy extends Horde_Text_Diff_Op_Base +{ + public function __construct($orig, $final = false) + { + if (!is_array($final)) { + $final = $orig; + } + $this->orig = $orig; + $this->final = $final; + } + + public function reverse() + { + return new Horde_Text_Diff_Op_Copy($this->final, $this->orig); + } +} diff --git a/src/Horde/Text/Diff/Op/Delete.php b/src/Horde/Text/Diff/Op/Delete.php new file mode 100644 index 00000000..3ed50ea5 --- /dev/null +++ b/src/Horde/Text/Diff/Op/Delete.php @@ -0,0 +1,27 @@ +, and is used/adapted with his permission. + * + * Copyright 2004 Geoffrey T. Dairiki + * Copyright 2004-2017 Horde LLC (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://www.horde.org/licenses/lgpl21. + * + * @package Text_Diff + * @author Geoffrey T. Dairiki + */ +class Horde_Text_Diff_Op_Delete extends Horde_Text_Diff_Op_Base +{ + public function __construct($lines) + { + $this->orig = $lines; + $this->final = false; + } + + public function reverse() + { + return new Horde_Text_Diff_Op_Add($this->orig); + } +} diff --git a/src/Horde/Text/Diff/Renderer.php b/src/Horde/Text/Diff/Renderer.php new file mode 100644 index 00000000..d1ed2791 --- /dev/null +++ b/src/Horde/Text/Diff/Renderer.php @@ -0,0 +1,234 @@ + $value) { + $v = '_' . $param; + if (isset($this->$v)) { + $this->$v = $value; + } + } + } + + /** + * Get any renderer parameters. + * + * @return array All parameters of this renderer object. + */ + public function getParams() + { + $params = array(); + foreach (get_object_vars($this) as $k => $v) { + if ($k[0] == '_') { + $params[substr($k, 1)] = $v; + } + } + + return $params; + } + + /** + * Renders a diff. + * + * @param Horde_Text_Diff $diff A Horde_Text_Diff object. + * + * @return string The formatted output. + */ + public function render($diff) + { + $xi = $yi = 1; + $block = false; + $context = array(); + + $nlead = $this->_leading_context_lines; + $ntrail = $this->_trailing_context_lines; + + $output = $this->_startDiff(); + + $diffs = $diff->getDiff(); + foreach ($diffs as $i => $edit) { + /* If these are unchanged (copied) lines, and we want to keep + * leading or trailing context lines, extract them from the copy + * block. */ + if ($edit instanceof Horde_Text_Diff_Op_Copy) { + /* Do we have any diff blocks yet? */ + if (is_array($block)) { + /* How many lines to keep as context from the copy + * block. */ + $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail; + if (count($edit->orig) <= $keep) { + /* We have less lines in the block than we want for + * context => keep the whole block. */ + $block[] = $edit; + } else { + if ($ntrail) { + /* Create a new block with as many lines as we need + * for the trailing context. */ + $context = array_slice($edit->orig, 0, $ntrail); + $block[] = new Horde_Text_Diff_Op_Copy($context); + } + /* @todo */ + $output .= $this->_block($x0, $ntrail + $xi - $x0, + $y0, $ntrail + $yi - $y0, + $block); + $block = false; + } + } + /* Keep the copy block as the context for the next block. */ + $context = $edit->orig; + } else { + /* Don't we have any diff blocks yet? */ + if (!is_array($block)) { + /* Extract context lines from the preceding copy block. */ + $context = array_slice($context, count($context) - $nlead); + $x0 = $xi - count($context); + $y0 = $yi - count($context); + $block = array(); + if ($context) { + $block[] = new Horde_Text_Diff_Op_Copy($context); + } + } + $block[] = $edit; + } + + if ($edit->orig) { + $xi += count($edit->orig); + } + if ($edit->final) { + $yi += count($edit->final); + } + } + + if (is_array($block)) { + $output .= $this->_block($x0, $xi - $x0, + $y0, $yi - $y0, + $block); + } + + return $output . $this->_endDiff(); + } + + protected function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) + { + $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen)); + + foreach ($edits as $edit) { + switch (get_class($edit)) { + case 'Horde_Text_Diff_Op_Copy': + $output .= $this->_context($edit->orig); + break; + + case 'Horde_Text_Diff_Op_Add': + $output .= $this->_added($edit->final); + break; + + case 'Horde_Text_Diff_Op_Delete': + $output .= $this->_deleted($edit->orig); + break; + + case 'Horde_Text_Diff_Op_Change': + $output .= $this->_changed($edit->orig, $edit->final); + break; + } + } + + return $output . $this->_endBlock(); + } + + protected function _startDiff() + { + return ''; + } + + protected function _endDiff() + { + return ''; + } + + protected function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + { + if ($xlen > 1) { + $xbeg .= ',' . ($xbeg + $xlen - 1); + } + if ($ylen > 1) { + $ybeg .= ',' . ($ybeg + $ylen - 1); + } + + // this matches the GNU Diff behaviour + if ($xlen && !$ylen) { + $ybeg--; + } elseif (!$xlen) { + $xbeg--; + } + + return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg; + } + + protected function _startBlock($header) + { + return $header . "\n"; + } + + protected function _endBlock() + { + return ''; + } + + protected function _lines($lines, $prefix = ' ') + { + return $prefix . implode("\n$prefix", $lines) . "\n"; + } + + protected function _context($lines) + { + return $this->_lines($lines, ' '); + } + + protected function _added($lines) + { + return $this->_lines($lines, '> '); + } + + protected function _deleted($lines) + { + return $this->_lines($lines, '< '); + } + + protected function _changed($orig, $final) + { + return $this->_deleted($orig) . "---\n" . $this->_added($final); + } +} diff --git a/src/Horde/Text/Diff/Renderer/Context.php b/src/Horde/Text/Diff/Renderer/Context.php new file mode 100644 index 00000000..8e31c44c --- /dev/null +++ b/src/Horde/Text/Diff/Renderer/Context.php @@ -0,0 +1,68 @@ +_second_block = "--- $ybeg ----\n"; + return "***************\n*** $xbeg ****"; + } + + protected function _endBlock() + { + return $this->_second_block; + } + + protected function _context($lines) + { + $this->_second_block .= $this->_lines($lines, ' '); + return $this->_lines($lines, ' '); + } + + protected function _added($lines) + { + $this->_second_block .= $this->_lines($lines, '+ '); + return ''; + } + + protected function _deleted($lines) + { + return $this->_lines($lines, '- '); + } + + protected function _changed($orig, $final) + { + $this->_second_block .= $this->_lines($final, '! '); + return $this->_lines($orig, '! '); + } + +} diff --git a/src/Horde/Text/Diff/Renderer/Inline.php b/src/Horde/Text/Diff/Renderer/Inline.php new file mode 100644 index 00000000..cdf3ba98 --- /dev/null +++ b/src/Horde/Text/Diff/Renderer/Inline.php @@ -0,0 +1,193 @@ +'; + + /** + * Suffix for inserted text. + * + * @var string + */ + protected $_ins_suffix = ''; + + /** + * Prefix for deleted text. + * + * @var string + */ + protected $_del_prefix = ''; + + /** + * Suffix for deleted text. + * + * @var string + */ + protected $_del_suffix = ''; + + /** + * Header for each change block. + * + * @var string + */ + protected $_block_header = ''; + + /** + * Whether to split down to character-level. + * + * @var boolean + */ + protected $_split_characters = false; + + /** + * What are we currently splitting on? Used to recurse to show word-level + * or character-level changes. + * + * @var string + */ + protected $_split_level = 'lines'; + + protected function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + { + return $this->_block_header; + } + + protected function _startBlock($header) + { + return $header; + } + + protected function _lines($lines, $prefix = ' ', $encode = true) + { + if ($encode) { + array_walk($lines, array(&$this, '_encode')); + } + + if ($this->_split_level == 'lines') { + return implode("\n", $lines) . "\n"; + } else { + return implode('', $lines); + } + } + + protected function _added($lines) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_ins_prefix . $lines[0]; + $lines[count($lines) - 1] .= $this->_ins_suffix; + return $this->_lines($lines, ' ', false); + } + + protected function _deleted($lines, $words = false) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_del_prefix . $lines[0]; + $lines[count($lines) - 1] .= $this->_del_suffix; + return $this->_lines($lines, ' ', false); + } + + protected function _changed($orig, $final) + { + /* If we've already split on characters, just display. */ + if ($this->_split_level == 'characters') { + return $this->_deleted($orig) + . $this->_added($final); + } + + /* If we've already split on words, just display. */ + if ($this->_split_level == 'words') { + $prefix = ''; + while ($orig[0] !== false && $final[0] !== false && + substr($orig[0], 0, 1) == ' ' && + substr($final[0], 0, 1) == ' ') { + $prefix .= substr($orig[0], 0, 1); + $orig[0] = substr($orig[0], 1); + $final[0] = substr($final[0], 1); + } + return $prefix . $this->_deleted($orig) . $this->_added($final); + } + + $text1 = implode("\n", $orig); + $text2 = implode("\n", $final); + + /* Non-printing newline marker. */ + $nl = "\0"; + + if ($this->_split_characters) { + $diff = new Horde_Text_Diff('native', + array(preg_split('//u', str_replace("\n", $nl, $text1)), + preg_split('//u', str_replace("\n", $nl, $text2)))); + } else { + /* We want to split on word boundaries, but we need to preserve + * whitespace as well. Therefore we split on words, but include + * all blocks of whitespace in the wordlist. */ + $diff = new Horde_Text_Diff('native', + array($this->_splitOnWords($text1, $nl), + $this->_splitOnWords($text2, $nl))); + } + + /* Get the diff in inline format. */ + $renderer = new Horde_Text_Diff_Renderer_inline + (array_merge($this->getParams(), + array('split_level' => $this->_split_characters ? 'characters' : 'words'))); + + /* Run the diff and get the output. */ + return str_replace($nl, "\n", $renderer->render($diff)) . "\n"; + } + + protected function _splitOnWords($string, $newlineEscape = "\n") + { + // Ignore \0; otherwise the while loop will never finish. + $string = str_replace("\0", '', $string); + + $words = array(); + $length = strlen($string); + $pos = 0; + + while ($pos < $length) { + // Eat a word with any preceding whitespace. + $spaces = strspn(substr($string, $pos), " \n"); + $nextpos = strcspn(substr($string, $pos + $spaces), " \n"); + $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos)); + $pos += $spaces + $nextpos; + } + + return $words; + } + + protected function _encode(&$string) + { + $string = htmlspecialchars($string); + } +} diff --git a/src/Horde/Text/Diff/Renderer/Unified.php b/src/Horde/Text/Diff/Renderer/Unified.php new file mode 100644 index 00000000..c5ab4b44 --- /dev/null +++ b/src/Horde/Text/Diff/Renderer/Unified.php @@ -0,0 +1,57 @@ +_lines($lines, ' '); + } + + protected function _added($lines) + { + return $this->_lines($lines, '+'); + } + + protected function _deleted($lines) + { + return $this->_lines($lines, '-'); + } + + protected function _changed($orig, $final) + { + return $this->_deleted($orig) . $this->_added($final); + } +} diff --git a/src/Horde/Text/Diff/Renderer/Unified/Colored.php b/src/Horde/Text/Diff/Renderer/Unified/Colored.php new file mode 100644 index 00000000..0f71c31a --- /dev/null +++ b/src/Horde/Text/Diff/Renderer/Unified/Colored.php @@ -0,0 +1,67 @@ + + * @category Horde + * @license http://www.horde.org/licenses/lgpl21 LGPL + * @package Text_Diff + */ + +/** + * "Unified" diff renderer with output coloring. + * + * @author Jan Schneider + * @category Horde + * @copyright 2017 Horde LLC + * @license http://www.horde.org/licenses/lgpl21 LGPL + * @package Text_Diff + */ +class Horde_Text_Diff_Renderer_Unified_Colored +extends Horde_Text_Diff_Renderer_Unified +{ + /** + * CLI handler. + * + * Contrary to the name, it supports color highlighting for HTML too. + * + * @var Horde_Cli + */ + protected $_cli; + + /** + * Constructor. + */ + public function __construct($params = array()) + { + if (!isset($params['cli'])) { + throw new BadMethodCallException('CLI handler is missing'); + } + parent::__construct($params); + $this->_cli = $params['cli']; + } + + protected function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + { + return $this->_cli->color( + 'lightmagenta', parent::_blockHeader($xbeg, $xlen, $ybeg, $ylen) + ); + } + + protected function _added($lines) + { + return $this->_cli->color( + 'lightgreen', parent::_added($lines) + ); + } + + protected function _deleted($lines) + { + return $this->_cli->color( + 'lightred', parent::_deleted($lines) + ); + } +} diff --git a/src/Horde/Text/Diff/ThreeWay.php b/src/Horde/Text/Diff/ThreeWay.php new file mode 100644 index 00000000..81fbde03 --- /dev/null +++ b/src/Horde/Text/Diff/ThreeWay.php @@ -0,0 +1,143 @@ + + */ +class Horde_Text_Diff_ThreeWay +{ + /** + * Array of changes. + * + * @var array + */ + protected $_edits; + + /** + * Conflict counter. + * + * @var integer + */ + protected $_conflictingBlocks = 0; + + /** + * Computes diff between 3 sequences of strings. + * + * @param array $orig The original lines to use. + * @param array $final1 The first version to compare to. + * @param array $final2 The second version to compare to. + */ + public function __construct($orig, $final1, $final2) + { + if (extension_loaded('xdiff')) { + $engine = new Horde_Text_Diff_Engine_Xdiff(); + } else { + $engine = new Horde_Text_Diff_Engine_Native(); + } + + $this->_edits = $this->_diff3($engine->diff($orig, $final1), + $engine->diff($orig, $final2)); + } + + /** + */ + public function mergedOutput($label1 = false, $label2 = false) + { + $lines = array(); + foreach ($this->_edits as $edit) { + if ($edit->isConflict()) { + /* FIXME: this should probably be moved somewhere else. */ + $lines = array_merge($lines, + array('<<<<<<<' . ($label1 ? ' ' . $label1 : '')), + $edit->final1, + array("======="), + $edit->final2, + array('>>>>>>>' . ($label2 ? ' ' . $label2 : ''))); + $this->_conflictingBlocks++; + } else { + $lines = array_merge($lines, $edit->merged()); + } + } + + return $lines; + } + + /** + */ + protected function _diff3($edits1, $edits2) + { + $edits = array(); + $bb = new Horde_Text_Diff_ThreeWay_BlockBuilder(); + + $e1 = current($edits1); + $e2 = current($edits2); + while ($e1 || $e2) { + if ($e1 && $e2 && + $e1 instanceof Horde_Text_Diff_Op_Copy && + $e2 instanceof Horde_Text_Diff_Op_Copy) { + /* We have copy blocks from both diffs. This is the (only) + * time we want to emit a diff3 copy block. Flush current + * diff3 diff block, if any. */ + if ($edit = $bb->finish()) { + $edits[] = $edit; + } + + $ncopy = min($e1->norig(), $e2->norig()); + assert($ncopy > 0); + $edits[] = new Horde_Text_Diff_ThreeWay_Op_Copy(array_slice($e1->orig, 0, $ncopy)); + + if ($e1->norig() > $ncopy) { + array_splice($e1->orig, 0, $ncopy); + array_splice($e1->final, 0, $ncopy); + } else { + $e1 = next($edits1); + } + + if ($e2->norig() > $ncopy) { + array_splice($e2->orig, 0, $ncopy); + array_splice($e2->final, 0, $ncopy); + } else { + $e2 = next($edits2); + } + } else { + if ($e1 && $e2) { + if ($e1->orig && $e2->orig) { + $norig = min($e1->norig(), $e2->norig()); + $orig = array_splice($e1->orig, 0, $norig); + array_splice($e2->orig, 0, $norig); + $bb->input($orig); + } + + if ($e1 instanceof Horde_Text_Diff_Op_Copy) { + $bb->out1(array_splice($e1->final, 0, $norig)); + } + + if ($e2 instanceof Horde_Text_Diff_Op_Copy) { + $bb->out2(array_splice($e2->final, 0, $norig)); + } + } + + if ($e1 && ! $e1->orig) { + $bb->out1($e1->final); + $e1 = next($edits1); + } + if ($e2 && ! $e2->orig) { + $bb->out2($e2->final); + $e2 = next($edits2); + } + } + } + + if ($edit = $bb->finish()) { + $edits[] = $edit; + } + + return $edits; + } +} diff --git a/src/Horde/Text/Diff/ThreeWay/BlockBuilder.php b/src/Horde/Text/Diff/ThreeWay/BlockBuilder.php new file mode 100644 index 00000000..08b44942 --- /dev/null +++ b/src/Horde/Text/Diff/ThreeWay/BlockBuilder.php @@ -0,0 +1,64 @@ + + */ +class Horde_Text_Diff_ThreeWay_BlockBuilder +{ + public function __construct() + { + $this->_init(); + } + + public function input($lines) + { + if ($lines) { + $this->_append($this->orig, $lines); + } + } + + public function out1($lines) + { + if ($lines) { + $this->_append($this->final1, $lines); + } + } + + public function out2($lines) + { + if ($lines) { + $this->_append($this->final2, $lines); + } + } + + public function isEmpty() + { + return !$this->orig && !$this->final1 && !$this->final2; + } + + public function finish() + { + if ($this->isEmpty()) { + return false; + } else { + $edit = new Horde_Text_Diff_ThreeWay_Op_Base($this->orig, $this->final1, $this->final2); + $this->_init(); + return $edit; + } + } + + protected function _init() + { + $this->orig = $this->final1 = $this->final2 = array(); + } + + protected function _append(&$array, $lines) + { + array_splice($array, sizeof($array), 0, $lines); + } +} diff --git a/src/Horde/Text/Diff/ThreeWay/Op/Base.php b/src/Horde/Text/Diff/ThreeWay/Op/Base.php new file mode 100644 index 00000000..d62a1764 --- /dev/null +++ b/src/Horde/Text/Diff/ThreeWay/Op/Base.php @@ -0,0 +1,41 @@ + + */ +class Horde_Text_Diff_ThreeWay_Op_Base +{ + public function __construct($orig = false, $final1 = false, $final2 = false) + { + $this->orig = $orig ? $orig : array(); + $this->final1 = $final1 ? $final1 : array(); + $this->final2 = $final2 ? $final2 : array(); + } + + public function merged() + { + if (!isset($this->_merged)) { + if ($this->final1 === $this->final2) { + $this->_merged = &$this->final1; + } elseif ($this->final1 === $this->orig) { + $this->_merged = &$this->final2; + } elseif ($this->final2 === $this->orig) { + $this->_merged = &$this->final1; + } else { + $this->_merged = false; + } + } + + return $this->_merged; + } + + public function isConflict() + { + return $this->merged() === false; + } +} diff --git a/src/Horde/Text/Diff/ThreeWay/Op/Copy.php b/src/Horde/Text/Diff/ThreeWay/Op/Copy.php new file mode 100644 index 00000000..c5f9cb8b --- /dev/null +++ b/src/Horde/Text/Diff/ThreeWay/Op/Copy.php @@ -0,0 +1,29 @@ + + */ +class Horde_Text_Diff_ThreeWay_Op_Copy extends Horde_Text_Diff_ThreeWay_Op_Base +{ + public function __construct($lines = false) + { + $this->orig = $lines ? $lines : array(); + $this->final1 = &$this->orig; + $this->final2 = &$this->orig; + } + + public function merged() + { + return $this->orig; + } + + public function isConflict() + { + return false; + } +} diff --git a/src/Utils/Diff.php b/src/Utils/Diff.php new file mode 100644 index 00000000..30959f08 --- /dev/null +++ b/src/Utils/Diff.php @@ -0,0 +1,77 @@ +diff = $d; + parent::__construct(); + } + + public function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + { + $removed = $xlen - $ylen; + if ($removed > 0) { + return 'Line '.$xbeg.' (now '.$ybeg.'), was '.$xlen.' lines, now '.$ylen.' lines'; + } + } + + public function _added($lines) + { + self::escapeHTML($lines); + + return ' '.implode(''."\n".' ', $lines).''; + } + + public function _context($lines) + { + self::escapeHTML($lines); + + return "\n" . parent::_context($lines); + } + + public function _deleted($lines) + { + self::escapeHTML($lines); + + return ' '.implode(''."\n".' ', $lines).''; + } + + public function _changed($orig, $final) + { + return $this->_deleted($orig)."\n".$this->_added($final); + } + + public function render($diff) + { + return parent::render($this->diff); + } + + protected static function escapeHTML(&$lines) + { + array_walk($lines, function(&$a, $b) { + $a = htmlspecialchars($a); + }); + } +} diff --git a/www/patch-display.php b/www/patch-display.php index 57dd490b..5a027bbf 100644 --- a/www/patch-display.php +++ b/www/patch-display.php @@ -5,6 +5,7 @@ use App\Repository\PatchRepository; use App\Utils\PatchTracker; use App\Utils\Uploader; +use App\Utils\Diff; session_start(); @@ -109,12 +110,13 @@ exit; } - require_once "{$ROOT_DIR}/include/classes/bug_diff_renderer.php"; - assert_options(ASSERT_WARNING, 0); - $d = new Text_Diff($orig = file($old), $now = file($new)); - $diff = new Bug_Diff_Renderer($d); + + $d = new \Horde_Text_Diff('auto', [file($old), file($new)]); + $diff = new Diff($d); + include "{$ROOT_DIR}/templates/patchdiff.php"; + response_footer(); exit; } From 102df8157547bb44eeba9e10e646ed0b6ccb48f7 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Fri, 25 Jan 2019 02:40:54 +0100 Subject: [PATCH 178/277] Bump Composer dependencies --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 5296c0b1..a2bc849a 100644 --- a/composer.lock +++ b/composer.lock @@ -680,16 +680,16 @@ }, { "name": "phpunit/phpunit", - "version": "7.5.1", + "version": "7.5.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c23d78776ad415d5506e0679723cb461d71f488f" + "reference": "7c89093bd00f7d5ddf0ab81dee04f801416b4944" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c23d78776ad415d5506e0679723cb461d71f488f", - "reference": "c23d78776ad415d5506e0679723cb461d71f488f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7c89093bd00f7d5ddf0ab81dee04f801416b4944", + "reference": "7c89093bd00f7d5ddf0ab81dee04f801416b4944", "shasum": "" }, "require": { @@ -760,7 +760,7 @@ "testing", "xunit" ], - "time": "2018-12-12T07:20:32+00:00" + "time": "2019-01-15T08:19:08+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1370,7 +1370,7 @@ }, { "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "email": "backendtea@gmail.com" } ], "description": "Symfony polyfill for ctype functions", From 5cd2630a86cd4feaacdd1353f246b234dfe75dc1 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 24 Dec 2018 03:58:58 +0100 Subject: [PATCH 179/277] Add dependency injection container This patch introduces a dependency injection container for the PHP bug tracker application. Container deals with the creation of all service classes and additionally provides retrieving of configuration parameters. Service classes are used everywhere in the app - from accessing database to uploading files. Configuration parameters include infrastructure configuration (database credentials...) and application level configuration (directories locations...). Container is compatible with the PSR-11 container interface defined so it is simple to quickly understand its usage. Advanced features such as autowiring are not included in this phase. --- README.md | 1 + config/container.php | 84 +++++++++++++ config/parameters.php | 73 +++++++++++ docs/README.md | 1 + docs/container.md | 119 ++++++++++++++++++ docs/templates.md | 2 +- include/prepend.php | 85 ++++++------- include/query.php | 2 +- scripts/cron/email-assigned | 2 +- scripts/cron/no-feedback | 4 +- src/Container/Container.php | 101 +++++++++++++++ src/Container/ContainerInterface.php | 34 +++++ .../Exception/ContainerException.php | 10 ++ .../Exception/ContainerExceptionInterface.php | 10 ++ .../Exception/EntryNotFoundException.php | 7 ++ .../Exception/NotFoundExceptionInterface.php | 10 ++ src/Repository/PatchRepository.php | 4 +- src/Utils/PatchTracker.php | 6 +- tests/Container/ContainerTest.php | 109 ++++++++++++++++ tests/Container/MockDependency.php | 21 ++++ tests/Container/MockService.php | 32 +++++ www/admin/index.php | 2 +- www/api.php | 2 +- www/bug-pwd-finder.php | 2 +- www/bug.php | 16 +-- www/fix.php | 4 +- www/gh-pull-add.php | 10 +- www/index.php | 2 +- www/lstats.php | 2 +- www/patch-add.php | 10 +- www/patch-display.php | 10 +- www/quick-fix-desc.php | 2 +- www/report.php | 10 +- www/rpc.php | 2 +- www/rss/bug.php | 4 +- www/stats.php | 2 +- www/vote.php | 4 +- 37 files changed, 697 insertions(+), 104 deletions(-) create mode 100644 config/container.php create mode 100644 config/parameters.php create mode 100644 docs/container.md create mode 100644 src/Container/Container.php create mode 100644 src/Container/ContainerInterface.php create mode 100644 src/Container/Exception/ContainerException.php create mode 100644 src/Container/Exception/ContainerExceptionInterface.php create mode 100644 src/Container/Exception/EntryNotFoundException.php create mode 100644 src/Container/Exception/NotFoundExceptionInterface.php create mode 100644 tests/Container/ContainerTest.php create mode 100644 tests/Container/MockDependency.php create mode 100644 tests/Container/MockService.php diff --git a/README.md b/README.md index 275f64d8..d3421a25 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ Source code of this application is structured in the following directories: ```bash / ├─ .git/ # Git configuration and source directory + ├─ config/ # Application configuration parameters, services... ├─ docs/ # Application documentation └─ include/ # Application helper functions and configuration ├─ prepend.php # Autoloader, DB connection, container, app initialization diff --git a/config/container.php b/config/container.php new file mode 100644 index 00000000..5cf29795 --- /dev/null +++ b/config/container.php @@ -0,0 +1,84 @@ +set(\PDO::class, function ($c) { + return new \PDO( + 'mysql:host='.$c->get('db_host').';dbname='.$c->get('db_name').';charset=utf8', + $c->get('db_user'), + $c->get('db_password'), + [ + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, + \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, + \PDO::ATTR_EMULATE_PREPARES => false, + \PDO::ATTR_STATEMENT_CLASS => [App\Database\Statement::class], + ] + ); +}); + +$container->set(App\Repository\BugRepository::class, function ($c) { + return new App\Repository\BugRepository($c->get(\PDO::class)); +}); + +$container->set(App\Repository\CommentRepository::class, function ($c) { + return new App\Repository\CommentRepository($c->get(\PDO::class)); +}); + +$container->set(App\Repository\ObsoletePatchRepository::class, function ($c) { + return new App\Repository\ObsoletePatchRepository($c->get(\PDO::class)); +}); + +$container->set(App\Repository\PackageRepository::class, function ($c) { + return new App\Repository\PackageRepository($c->get(\PDO::class)); +}); + +$container->set(App\Repository\PatchRepository::class, function ($c) { + return new App\Repository\PatchRepository($c->get(\PDO::class), $c->get('uploads_dir')); +}); + +$container->set(App\Repository\PullRequestRepository::class, function ($c) { + return new App\Repository\PullRequestRepository($c->get(\PDO::class)); +}); + +$container->set(App\Repository\ReasonRepository::class, function ($c) { + return new App\Repository\ReasonRepository($c->get(\PDO::class)); +}); + +$container->set(App\Repository\VoteRepository::class, function ($c) { + return new App\Repository\VoteRepository($c->get(\PDO::class)); +}); + +$container->set(App\Template\Engine::class, function ($c) { + return new App\Template\Engine($c->get('templates_dir')); +}); + +$container->set(App\Utils\Captcha::class, function ($c) { + return new App\Utils\Captcha(); +}); + +$container->set(App\Utils\GitHub::class, function ($c) { + return new App\Utils\GitHub($c->get(\PDO::class)); +}); + +$container->set(App\Utils\PatchTracker::class, function ($c) { + return new App\Utils\PatchTracker( + $c->get(\PDO::class), + $c->get(App\Utils\Uploader::class), + $c->get('uploads_dir') + ); +}); + +$container->set(App\Utils\Uploader::class, function ($c) { + return new App\Utils\Uploader(); +}); + +return $container; diff --git a/config/parameters.php b/config/parameters.php new file mode 100644 index 00000000..9ff0d78a --- /dev/null +++ b/config/parameters.php @@ -0,0 +1,73 @@ + (defined('DEVBOX') && true === DEVBOX) ? 'dev' : 'prod', + + /** + * Site scheme - http or https. + */ + 'site_scheme' => $site_data['method'], + + /** + * Site URL. + */ + 'site_url' => $site_data['url'], + + /** + * Site base path if present. Part that comes after the domain + * https://bugs.php.net/basedir/ + */ + 'basedir' => $site_data['basedir'], + + /** + * Database username. + */ + 'db_user' => $site_data['db_user'], + + /** + * Database password. + */ + 'db_password' => $site_data['db_pass'], + + /** + * Database host name. + */ + 'db_host' => $site_data['db_host'], + + /** + * Database name. + */ + 'db_name' => $site_data['db'], + + /** + * Main email of the public mailing list. + */ + 'email'=> $site_data['email'], + + /** + * Email of the public mailing list for documentation related bugs. + */ + 'doc_email' => $site_data['doc_email'], + + /** + * Security email - emails sent to this are not visible in public. + */ + 'security_email' => $site_data['security_email'], + + /** + * Uploads directory location. + */ + 'uploads_dir' => $site_data['patch_tmp'], + + /** + * Templates directory. + */ + 'templates_dir' => __DIR__.'/../templates', +]; diff --git a/docs/README.md b/docs/README.md index 1cc4c00f..273ab36f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,3 +1,4 @@ # Application documentation * [Templates](/docs/templates.md) +* [Dependency injection container](/docs/container.md) diff --git a/docs/container.md b/docs/container.md new file mode 100644 index 00000000..2403f579 --- /dev/null +++ b/docs/container.md @@ -0,0 +1,119 @@ +# Dependency injection container + +The PHP bug tracker application ships with a simplistic dependency injection +container which can create services and retrieves configuration values. + +Services are one of the more frequently used objects everywhere across the +application. For example, service for database access, utility service for +uploading files, data generators, API clients, and similar. + +## Dependency injection + +Dependencies between classes are injected using either constructor, or via a +method call such as setter. + +```php +class Repository +{ + private $pdo; + + public function __construct(\PDO $pdo) + { + $this->pdo = $pdo; + } + + public function getData(): array + { + return $this->pdo->query("SELECT * FROM table")->fetchAll(); + } +} + +$pdo = new \PDO( + 'mysql:host=localhost;dbname=bugs;charset=utf8', 'nobody', 'password', + [ + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, + \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, + \PDO::ATTR_EMULATE_PREPARES => false, + ] +); + +$repository = new Repository($pdo); +$data = $repository->getData(); +``` + +The `$pdo` object in the example is a dependency which is injected via the +constructor. + +Dependency injection container further provides a more efficient creation of +such dependencies and services: + +```php +$container = require_once __DIR__.'/../config/container.php'; + +$data = $container->get(Repository::class)->getData(); +``` + +## Configuration + +Configuration parameters include infrastructure configuration (database +credentials...) and application level configuration (directories locations...). + +```php +// config/parameters.php + +return [ + 'parameter_key' => 'value', + + // ... +]; +``` + +Which can be retrieved by the container: + +```php +$value = $container->get('parameter_key'); +``` + +## Container definitions + +Each service class is manually defined: + +```php +// config/container.php + +// New container initialization with configuration parameters defined in a file. +$container = new Container(include __DIR__.'/parameters.php'); + +// Services are then defined using callables with a container argument $c. + +// Service with constructor arguments +$container->set(App\Foo::class, function ($c) { + return new App\Foo($c->get(App\Dependency::class)); +}); + +// Service with a setter method +$container->set(App\Foo\Bar::class, function ($c) { + $object = new App\Foo\Bar($c->get(App\Dependency::class)); + $object->setFoobar('argument'); + + return $object; +}); + +// Dependencies can be service classes or configuration parameters +$container->set(App\Foo\Bar::class, function ($c) { + return new App\Foo\Bar( + // Configuration parameter + $c->get('parameter_key')); + + // Calling method from another service + $c->get(App\Dependency::class)->methodName(); + ); +}); + +// Service with no dependencies +$container->set(App\Dependency::class, function ($c) { + return new App\Dependency(); +}); + +return $container; +``` diff --git a/docs/templates.md b/docs/templates.md index 66ca34d7..cea0976e 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -4,7 +4,7 @@ A simple template engine separates logic from the presentation and provides methods for creating nested templates and escaping strings to protect against too common XSS vulnerabilities. -It is initialized in the application bootstrap: +Template engine initialization: ```php $template = new App\Template\Engine(__DIR__.'/../path/to/templates'); diff --git a/include/prepend.php b/include/prepend.php index 49400f95..64b99fff 100644 --- a/include/prepend.php +++ b/include/prepend.php @@ -1,7 +1,6 @@ addClassmap('Horde_Text_Diff_Renderer', __DIR__.'/../src/Horde/Text/Diff/Renderer.php'); } +// Configuration $site = 'php'; $siteBig = 'PHP'; $ROOT_DIR = realpath(__DIR__ . '/../'); -$local_cfg = "{$ROOT_DIR}/local_config.php"; -if (file_exists($local_cfg)) { - require $local_cfg; +$localConfigFile = __DIR__.'/../local_config.php'; +if (file_exists($localConfigFile)) { + require $localConfigFile; } else { - $site_data = [ - 'method' => 'https', - 'url' => 'bugs.php.net', - 'basedir' => '', - 'email' => 'php-bugs@lists.php.net', - 'doc_email' => 'doc-bugs@lists.php.net', - 'security_email' => 'security@php.net', - 'db' => 'phpbugsdb', - 'db_user' => 'nobody', - 'db_pass' => '', - 'db_host' => 'localhost', - 'patch_tmp' => "{$ROOT_DIR}/uploads/patches/", - ]; - define('DEVBOX', false); -} -// CONFIG END - -// Configure errors based on the environment. -if (defined('DEVBOX') && true === DEVBOX) { - ini_set('display_errors', 1); -} else { - ini_set('display_errors', 0); + $site_data = [ + 'method' => 'https', + 'url' => 'bugs.php.net', + 'basedir' => '', + 'email' => 'php-bugs@lists.php.net', + 'doc_email' => 'doc-bugs@lists.php.net', + 'security_email' => 'security@php.net', + 'db' => 'phpbugsdb', + 'db_user' => 'nobody', + 'db_pass' => '', + 'db_host' => 'localhost', + 'patch_tmp' => __DIR__.'/../uploads/patches/', + ]; + define('DEVBOX', false); } if (!isset($site_data['security_email'])) { - $site_data['security_email'] = 'security@php.net'; + $site_data['security_email'] = 'security@php.net'; } // DO NOT EDIT ANYTHING BELOW THIS LINE, edit $site_data above instead! @@ -70,34 +62,31 @@ $docBugEmail = $site_data['doc_email']; $secBugEmail = $site_data['security_email']; $basedir = $site_data['basedir']; -define('BUG_PATCHTRACKER_TMPDIR', $site_data['patch_tmp']); -/** - * Obtain the functions and variables used throughout the bug system - */ -require_once "{$ROOT_DIR}/include/functions.php"; +// Container initialization +$container = require_once __DIR__.'/../config/container.php'; + +// Configure errors based on the environment. +if ('dev' === $container->get('env')) { + ini_set('display_errors', '1'); +} else { + ini_set('display_errors', '0'); +} + +// Obtain the functions and variables used throughout the bug system. +require_once __DIR__.'/functions.php'; -// Database connection with vanilla PDO to understand app architecture in no time -$dbh = new \PDO( - 'mysql:host='.$site_data['db_host'].';dbname='.$site_data['db'].';charset=utf8', - $site_data['db_user'], - $site_data['db_pass'], - [ - \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, - \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, - \PDO::ATTR_EMULATE_PREPARES => false, - \PDO::ATTR_STATEMENT_CLASS => [Statement::class], - ] -); +// Database connection with vanilla PDO to understand app architecture in no time. +$dbh = $container->get(\PDO::class); -// Last Updated.. +// Last updated. $tmp = filectime($_SERVER['SCRIPT_FILENAME']); $LAST_UPDATED = date('D M d H:i:s Y', $tmp - date('Z', $tmp)) . ' UTC'; // Initialize template engine. -$template = new Engine(__DIR__.'/../templates'); +$template = $container->get(Engine::class); $template->assign([ 'lastUpdated' => $LAST_UPDATED, - 'siteScheme' => $site_method, - 'siteUrl' => $site_url, + 'siteScheme' => $container->get('site_scheme'), + 'siteUrl' => $container->get('site_url'), ]); diff --git a/include/query.php b/include/query.php index 51ed8073..efe903b5 100644 --- a/include/query.php +++ b/include/query.php @@ -22,7 +22,7 @@ ]; // Fetch pseudo packages -$packageRepository = new PackageRepository($dbh); +$packageRepository = $container->get(PackageRepository::class); $pseudo_pkgs = $packageRepository->findAll(); // Setup input variables.. diff --git a/scripts/cron/email-assigned b/scripts/cron/email-assigned index 60456a6d..e8419624 100755 --- a/scripts/cron/email-assigned +++ b/scripts/cron/email-assigned @@ -10,7 +10,7 @@ require __DIR__ . '/../../include/prepend.php'; is a little odd that way. 'No Feedback' was once a part of this, but no longer: https://news.php.net/php.webmaster/8828 */ -foreach ((new BugRepository($dbh))->findAllAssigned() as $assigned => $binfos) { +foreach ($container->get(BugRepository::class)->findAllAssigned() as $assigned => $binfos) { $mbody = format_email_body($binfos); $email_user = $assigned . '@php.net'; diff --git a/scripts/cron/no-feedback b/scripts/cron/no-feedback index 653ab8d3..82c3deb6 100755 --- a/scripts/cron/no-feedback +++ b/scripts/cron/no-feedback @@ -12,11 +12,11 @@ require __DIR__.'/../../include/prepend.php'; $in = ['status' => 'No Feedback']; # Update relevant reports -$reasonRepository = new ReasonRepository($dbh); +$reasonRepository = $container->get(ReasonRepository::class); list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($site); -foreach ((new BugRepository($dbh))->findAllWithoutFeedback() as $bug) +foreach ($container->get(BugRepository::class)->findAllWithoutFeedback() as $bug) { list($mailto, $mailfrom, $bcc, $params) = get_package_mail($bug['package_name'], false, $bug['bug_type']); diff --git a/src/Container/Container.php b/src/Container/Container.php new file mode 100644 index 00000000..ebf2fadc --- /dev/null +++ b/src/Container/Container.php @@ -0,0 +1,101 @@ +entries = $configurations; + } + + /** + * Set service. + */ + public function set(string $key, $entry): void + { + $this->entries[$key] = $entry; + } + + /** + * Get entry. + * + * @return mixed + */ + public function get(string $id) + { + if (!$this->has($id)) { + throw new EntryNotFoundException($id.' entry not found.'); + } + + if (!isset($this->store[$id])) { + $this->store[$id] = $this->createEntry($id); + } + + return $this->store[$id]; + } + + /** + * Check if entry is available in the container. + */ + public function has(string $id): bool + { + return isset($this->entries[$id]); + } + + /** + * Create new entry - service or configuration parameter. + * + * @return mixed + */ + private function createEntry(string $id) + { + $entry = &$this->entries[$id]; + + // Entry is a configuration parameter. + if (!class_exists($id) && !is_callable($entry)) { + return $entry; + } + + // Entry is a service. + if (class_exists($id) && !is_callable($entry)) { + throw new ContainerException($id.' entry must be callable.'); + } elseif (class_exists($id) && isset($this->locks[$id])) { + throw new ContainerException($id.' entry contains a circular reference.'); + } + + $this->locks[$id] = true; + + return $entry($this); + } +} diff --git a/src/Container/ContainerInterface.php b/src/Container/ContainerInterface.php new file mode 100644 index 00000000..211ba7e7 --- /dev/null +++ b/src/Container/ContainerInterface.php @@ -0,0 +1,34 @@ +dbh = $dbh; - $this->uploadsDir = BUG_PATCHTRACKER_TMPDIR; + $this->uploadsDir = $uploadsDir; } /** diff --git a/src/Utils/PatchTracker.php b/src/Utils/PatchTracker.php index 028f9f1c..0e080f40 100644 --- a/src/Utils/PatchTracker.php +++ b/src/Utils/PatchTracker.php @@ -2,8 +2,6 @@ namespace App\Utils; -use App\Utils\Uploader; - /** * Service for handling uploaded patches. */ @@ -48,10 +46,10 @@ class PatchTracker /** * Class constructor. */ - public function __construct(\PDO $dbh, Uploader $uploader) + public function __construct(\PDO $dbh, Uploader $uploader, string $uploadsDir) { $this->dbh = $dbh; - $this->uploadsDir = BUG_PATCHTRACKER_TMPDIR; + $this->uploadsDir = $uploadsDir; $this->uploader = $uploader; $this->uploader->setMaxFileSize(self::MAX_FILE_SIZE); diff --git a/tests/Container/ContainerTest.php b/tests/Container/ContainerTest.php new file mode 100644 index 00000000..42e1a74c --- /dev/null +++ b/tests/Container/ContainerTest.php @@ -0,0 +1,109 @@ +set(MockService::class, function ($c) { + $service = new MockService($c->get(MockDependency::class), 'foo'); + $service->setProperty('group.param'); + + return $service; + }); + + $container->set(MockDependency::class, function ($c) { + return new MockDependency('group.param'); + }); + + // Check retrieval of service + $service = $container->get(MockService::class); + $this->assertInstanceOf(MockService::class, $service); + + // Check retrieval of dependency + $dependency = $container->get(MockDependency::class); + $this->assertInstanceOf(MockDependency::class, $dependency); + + // Check that the dependency has been reused + $this->assertSame($dependency, $service->getDependency()); + + // Check the service calls have initialized + $this->assertEquals('group.param', $service->getProperty()); + } + + public function testHas() + { + $container = new Container(); + + $this->assertFalse($container->has(MockDependency::class)); + + $container->set(MockDependency::class, function ($c) { + return new MockDependency('group.param'); + }); + + $this->assertFalse($container->has(MockService::class)); + $this->assertTrue($container->has(MockDependency::class)); + } + + /** + * @expectedException App\Container\Exception\EntryNotFoundException + */ + public function testServiceNotFound() + { + $container = new Container(); + $container->get('foo'); + } + + /** + * @expectedException App\Container\Exception\ContainerException + * @expectedExceptionMessage entry must be callable + */ + public function testBadServiceEntry() + { + $container = new Container(); + $container->set(\stdClass::class, ''); + $container->get(\stdClass::class); + } + + /** + * @expectedException App\Container\Exception\ContainerException + * @expectedExceptionMessage circular reference + */ + public function testCircularReference() + { + $container = new Container(); + + $container->set(MockService::class, function ($c) { + return new MockService($c->get(MockService::class)); + }); + + $container->set(MockService::class, function ($c) { + return new MockService($c->get(MockService::class)); + }); + + $container->get(MockService::class); + } + + public function testParametersAndServices() + { + $container = new Container([ + 'foo' => 'bar', + 'baz' => function ($c) { + return $c->get('foo'); + }, + ]); + + $this->assertTrue($container->has('foo')); + $this->assertTrue($container->has('baz')); + $this->assertEquals('bar', $container->get('foo')); + $this->assertEquals('bar', $container->get('baz')); + } +} diff --git a/tests/Container/MockDependency.php b/tests/Container/MockDependency.php new file mode 100644 index 00000000..dc2c3a93 --- /dev/null +++ b/tests/Container/MockDependency.php @@ -0,0 +1,21 @@ +parameter = $parameter; + } + + public function getParameter() + { + return $this->parameter; + } +} diff --git a/tests/Container/MockService.php b/tests/Container/MockService.php new file mode 100644 index 00000000..7d12d39b --- /dev/null +++ b/tests/Container/MockService.php @@ -0,0 +1,32 @@ +dependency = $dependency; + } + + public function getDependency() + { + return $this->dependency; + } + + public function setProperty($value) + { + $this->property = $value; + } + + public function getProperty() + { + return $this->property; + } +} diff --git a/www/admin/index.php b/www/admin/index.php index 1c2c810d..c71b90b7 100644 --- a/www/admin/index.php +++ b/www/admin/index.php @@ -53,7 +53,7 @@ } elseif ($action === 'list_lists') { echo "
    \n"; - foreach ((new PackageRepository($dbh))->findLists() as $row) { + foreach ($container->get(PackageRepository::class)->findLists() as $row) { echo "
    ", $row['name'], ":
    \n
    ", mailto_list(explode(',', $row['list_email'])), "
    \n"; } echo "
    \n"; diff --git a/www/api.php b/www/api.php index 4fbf5bc1..9cdc36eb 100644 --- a/www/api.php +++ b/www/api.php @@ -15,7 +15,7 @@ $interval = isset($_GET['interval']) ? (int) $_GET['interval'] : 7; if ($type === 'docs' && $action === 'closed' && $interval) { - $commentRepository = new CommentRepository($dbh); + $commentRepository = $container->get(CommentRepository::class); $rows = $commentRepository->findDocsComments($interval); //@todo add error handling diff --git a/www/bug-pwd-finder.php b/www/bug-pwd-finder.php index f0aa2ba4..5e3fe003 100644 --- a/www/bug-pwd-finder.php +++ b/www/bug-pwd-finder.php @@ -10,7 +10,7 @@ // Start session (for captcha!) session_start(); -$captcha = new Captcha(); +$captcha = $container->get(Captcha::class); $errors = []; $success = false; diff --git a/www/bug.php b/www/bug.php index 0fae8922..e89acb8c 100644 --- a/www/bug.php +++ b/www/bug.php @@ -16,8 +16,8 @@ // Start session session_start(); -$obsoletePatchRepository = new ObsoletePatchRepository($dbh); -$patchRepository = new PatchRepository($dbh); +$obsoletePatchRepository = $container->get(ObsoletePatchRepository::class); +$patchRepository = $container->get(PatchRepository::class); define('SPAM_REJECT_MESSAGE', 'Your comment looks like SPAM by its content. Please consider rewording.'); $email = null; @@ -125,14 +125,14 @@ // captcha is not necessary if the user is logged in if (!$logged_in) { - $captcha = new Captcha(); + $captcha = $container->get(Captcha::class); } $trytoforce = isset($_POST['trytoforce']) ? (int) $_POST['trytoforce'] : 0; // fetch info about the bug into $bug if (!isset($bug)) { - $bugRepository = new BugRepository($dbh); + $bugRepository = $container->get(BugRepository::class); $bug = $bugRepository->findOneById($bug_id); } @@ -187,13 +187,13 @@ // Only fetch stuff when it's really needed if ($edit && $edit < 3) { - $packageRepository = new PackageRepository($dbh); + $packageRepository = $container->get(PackageRepository::class); $pseudo_pkgs = $packageRepository->findEnabled(); } // Fetch RESOLVE_REASONS array if ($edit === 1) { - $reasonRepository = new ReasonRepository($dbh); + $reasonRepository = $container->get(ReasonRepository::class); list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($project); } @@ -1096,7 +1096,7 @@ } echo "

    Add a Patch

    "; - $pullRequestRepository = new PullRequestRepository($dbh); + $pullRequestRepository = $container->get(PullRequestRepository::class); $pulls = $pullRequestRepository->findAllByBugId($bug_id); echo "

    Pull Requests

    \n"; @@ -1105,7 +1105,7 @@ } // Display comments -$commentRepository = new CommentRepository($dbh); +$commentRepository = $container->get(CommentRepository::class); $bug_comments = is_int($bug_id) ? $commentRepository->findByBugId($bug_id) : []; if ($show_bug_info && is_array($bug_comments) && count($bug_comments) && $bug['status'] !== 'Spam') { diff --git a/www/fix.php b/www/fix.php index 66f552d7..4a114873 100644 --- a/www/fix.php +++ b/www/fix.php @@ -20,7 +20,7 @@ bugs_authenticate($user, $pw, $logged_in, $user_flags); // fetch info about the bug into $bug -$bugRepository = new BugRepository($dbh); +$bugRepository = $container->get(BugRepository::class); $bug = $bugRepository->findOneById($bug_id); if (!is_array($bug)) { @@ -39,7 +39,7 @@ $project = !empty($_GET['project']) ? $_GET['project'] : false; -$reasonRepository = new ReasonRepository($dbh); +$reasonRepository = $container->get(ReasonRepository::class); list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($site); // Handle reason / comments diff --git a/www/gh-pull-add.php b/www/gh-pull-add.php index 4e31f5f2..3f1d91a8 100644 --- a/www/gh-pull-add.php +++ b/www/gh-pull-add.php @@ -27,7 +27,7 @@ exit; } -$bugRepository = new BugRepository($dbh); +$bugRepository = $container->get(BugRepository::class); if (!($buginfo = $bugRepository->findOneById($bug_id))) { response_header('Error :: invalid bug selected'); @@ -40,7 +40,7 @@ // captcha is not necessary if the user is logged in if (!$logged_in) { - $captcha = new Captcha(); + $captcha = $container->get(Captcha::class); } $show_bug_info = bugs_has_access($bug_id, $buginfo, $pw, $user_flags); @@ -52,8 +52,8 @@ exit; } -$pullinfo = new GitHub($dbh); -$pullRequestRepository = new PullRequestRepository($dbh); +$pullinfo = $container->get(GitHub::class); +$pullRequestRepository = $container->get(PullRequestRepository::class); if (isset($_POST['addpull'])) { $errors = []; @@ -104,7 +104,7 @@ $errors[] = 'This pull request is already added.'; } - if (DEVBOX) { + if ('dev' === $container->get('env')) { $errors[] = $e->getMessage(); } } diff --git a/www/index.php b/www/index.php index 413e608a..34b69c96 100644 --- a/www/index.php +++ b/www/index.php @@ -40,7 +40,7 @@ } if ('/random' === $_SERVER['REQUEST_URI']) { - $id = (new BugRepository($dbh))->findRandom(); + $id = $container->get(BugRepository::class)->findRandom(); redirect('bug.php?id='.$id[0]); } diff --git a/www/lstats.php b/www/lstats.php index 2a22c4c7..765d44c8 100644 --- a/www/lstats.php +++ b/www/lstats.php @@ -46,7 +46,7 @@ function get_status_count ($status, $category = '') if (isset($_GET['per_category'])) { - $packageRepository = new PackageRepository($dbh); + $packageRepository = $container->get(PackageRepository::class); $pseudo_pkgs = $packageRepository->findAll($_GET['project'] ?? ''); $totals = []; diff --git a/www/patch-add.php b/www/patch-add.php index 6be26ba7..76e34f0a 100644 --- a/www/patch-add.php +++ b/www/patch-add.php @@ -4,14 +4,12 @@ use App\Repository\PatchRepository; use App\Utils\Captcha; use App\Utils\PatchTracker; -use App\Utils\Uploader; // Obtain common includes require_once '../include/prepend.php'; -$uploader = new Uploader(); -$patchTracker = new PatchTracker($dbh, $uploader); -$patchRepository = new PatchRepository($dbh); +$patchTracker = $container->get(PatchTracker::class); +$patchRepository = $container->get(PatchRepository::class); session_start(); @@ -33,7 +31,7 @@ exit; } -$bugRepository = new BugRepository($dbh); +$bugRepository = $container->get(BugRepository::class); if (!($buginfo = $bugRepository->findOneById($bug_id))) { response_header('Error :: invalid bug selected'); @@ -46,7 +44,7 @@ // captcha is not necessary if the user is logged in if (!$logged_in) { - $captcha = new Captcha(); + $captcha = $container->get(Captcha::class); } $show_bug_info = bugs_has_access($bug_id, $buginfo, $pw, $user_flags); diff --git a/www/patch-display.php b/www/patch-display.php index 5a027bbf..de2b9930 100644 --- a/www/patch-display.php +++ b/www/patch-display.php @@ -4,7 +4,6 @@ use App\Repository\ObsoletePatchRepository; use App\Repository\PatchRepository; use App\Utils\PatchTracker; -use App\Utils\Uploader; use App\Utils\Diff; session_start(); @@ -12,10 +11,9 @@ // Obtain common includes require_once '../include/prepend.php'; -$obsoletePatchRepository = new ObsoletePatchRepository($dbh); -$patchRepository = new PatchRepository($dbh); -$uploader = new Uploader(); -$patchTracker = new PatchTracker($dbh, $uploader); +$obsoletePatchRepository = $container->get(ObsoletePatchRepository::class); +$patchRepository = $container->get(PatchRepository::class); +$patchTracker = $container->get(PatchTracker::class); // Authenticate bugs_authenticate($user, $pw, $logged_in, $user_flags); @@ -40,7 +38,7 @@ $bug_id = (int) $_GET['bug_id']; } -$bugRepository = new BugRepository($dbh); +$bugRepository = $container->get(BugRepository::class); if (!($buginfo = $bugRepository->findOneById($bug_id))) { response_header('Error :: invalid bug selected'); diff --git a/www/quick-fix-desc.php b/www/quick-fix-desc.php index 01ff0989..208f7711 100644 --- a/www/quick-fix-desc.php +++ b/www/quick-fix-desc.php @@ -7,7 +7,7 @@ // Obtain common includes require_once '../include/prepend.php'; -$reasonRepository = new ReasonRepository($dbh); +$reasonRepository = $container->get(ReasonRepository::class); list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($site); // Authenticate diff --git a/www/report.php b/www/report.php index 9a7b61b5..dc556097 100644 --- a/www/report.php +++ b/www/report.php @@ -5,7 +5,6 @@ use App\Utils\Cache; use App\Utils\Captcha; use App\Utils\PatchTracker; -use App\Utils\Uploader; use App\Utils\Versions\Client; use App\Utils\Versions\Generator; @@ -19,7 +18,7 @@ $errors = []; $ok_to_submit_report = false; -$packageRepository = new PackageRepository($dbh); +$packageRepository = $container->get(PackageRepository::class); $pseudo_pkgs = $packageRepository->findEnabled($_GET['project'] ?? ''); // Authenticate @@ -33,7 +32,7 @@ // captcha is not necessary if the user is logged in if (!$logged_in) { - $captcha = new Captcha(); + $captcha = $container->get(Captcha::class); } $packageAffectedScript = << bug_JS; if ($edit == 1) { - $bug_JS .= ' + $bug_JS .= ' - '; + '; } @@ -1203,17 +1203,17 @@ function do_comment(nd) function mark_related_bugs($from, $comment_name, $ncomment) { - global $bug_id; + global $bug_id; - $related = get_ticket_links($ncomment); + $related = get_ticket_links($ncomment); - /** - * Adds a new comment on the related bug pointing to the current report - */ - foreach ($related as $bug) { - bugs_add_comment($bug, $from, $comment_name, - 'Related To: Bug #'. $bug_id, 'related'); - } + /** + * Adds a new comment on the related bug pointing to the current report + */ + foreach ($related as $bug) { + bugs_add_comment($bug, $from, $comment_name, + 'Related To: Bug #'. $bug_id, 'related'); + } } function link_to_people($email, $text) @@ -1228,53 +1228,53 @@ function link_to_people($email, $text) function output_note($com_id, $ts, $email, $comment, $comment_type, $comment_name, $is_hidden = false) { - global $edit, $bug_id, $dbh, $is_trusted_developer, $logged_in; + global $edit, $bug_id, $dbh, $is_trusted_developer, $logged_in; - $display = (!$is_hidden) ? '' : 'style="display:none;"'; + $display = (!$is_hidden) ? '' : 'style="display:none;"'; - echo "
    "; - echo ' '; - echo "[" , format_date($ts) , "] "; - echo link_to_people($email, spam_protect(htmlspecialchars($email))) , "\n"; + echo "
    "; + echo ' '; + echo "[" , format_date($ts) , "] "; + echo link_to_people($email, spam_protect(htmlspecialchars($email))) , "\n"; - switch ($comment_type) - { - case 'log': - echo "
    {$comment}
    "; - break; + switch ($comment_type) + { + case 'log': + echo "
    {$comment}
    "; + break; - default: - // Delete comment action only for trusted developers - echo ($edit == 1 && $com_id !== 0 && $is_trusted_developer) ? "[delete]\n" : ''; + default: + // Delete comment action only for trusted developers + echo ($edit == 1 && $com_id !== 0 && $is_trusted_developer) ? "[delete]\n" : ''; - $comment = make_ticket_links(addlinks($comment)); - echo "
    {$comment}\n
    \n"; - } + $comment = make_ticket_links(addlinks($comment)); + echo "
    {$comment}\n
    \n"; + } - echo '
    '; + echo '
    '; } function delete_comment($bug_id, $com_id) { - global $dbh; + global $dbh; - $dbh->prepare("DELETE FROM bugdb_comments WHERE bug='{$bug_id}' AND id='{$com_id}'")->execute(); + $dbh->prepare("DELETE FROM bugdb_comments WHERE bug='{$bug_id}' AND id='{$com_id}'")->execute(); } function control($num, $desc) { - global $bug_id, $edit; - - $str = "{$desc}"; - } else { - $str .= "'>{$desc}"; - } - return "{$str}\n"; + global $bug_id, $edit; + + $str = "{$desc}"; + } else { + $str .= "'>{$desc}"; + } + return "{$str}\n"; } function canvote($thanks, $status) { - return ($thanks != 4 && $thanks != 6 && $status != 'Closed' && $status != 'Not a bug' && $status != 'Duplicate'); + return ($thanks != 4 && $thanks != 6 && $status != 'Closed' && $status != 'Not a bug' && $status != 'Duplicate'); } diff --git a/www/bugs-generating-backtrace-win32.php b/www/bugs-generating-backtrace-win32.php index 9b5608dc..a12c26a9 100644 --- a/www/bugs-generating-backtrace-win32.php +++ b/www/bugs-generating-backtrace-win32.php @@ -16,8 +16,8 @@

    You'll need to install MS Visual Studio 2008, 2012 or later. You'll also need to

      -
    • either download the debug-pack for your PHP version from windows.php.net/download
    • -
    • or compile your own PHP with --enable-dbg-pack or --enable-debug
    • +
    • either download the debug-pack for your PHP version from windows.php.net/download
    • +
    • or compile your own PHP with --enable-dbg-pack or --enable-debug

    If you downloaded the debug-pack from the snaps site, extract it into @@ -45,8 +45,8 @@

    Generating backtrace, without compiler, on Windows

    You'll need:

    diff --git a/www/bugs-generating-backtrace.php b/www/bugs-generating-backtrace.php index caed58ff..5e6fafcc 100644 --- a/www/bugs-generating-backtrace.php +++ b/www/bugs-generating-backtrace.php @@ -33,42 +33,42 @@

    If you don't have a core file yet:

      -
    • - Remove any limits you may have on core dump size from your shell: -
        -
      • tcsh: unlimit coredumpsize
      • -
      • bash/sh: ulimit -c unlimited
      • -
      -
    • -
    • - Ensure that the directory in which you're running PHP, or the - PHP-enabled httpd, has write permissions for the user who's running PHP. -
    • -
    • - Cause PHP to crash: -
        -
      • PHP CGI: Simply run php with the script that crashes it
      • -
      • PHP Apache Module: Run httpd -X, and access the script that crashes PHP
      • -
      -
    • +
    • + Remove any limits you may have on core dump size from your shell: +
        +
      • tcsh: unlimit coredumpsize
      • +
      • bash/sh: ulimit -c unlimited
      • +
      +
    • +
    • + Ensure that the directory in which you're running PHP, or the + PHP-enabled httpd, has write permissions for the user who's running PHP. +
    • +
    • + Cause PHP to crash: +
        +
      • PHP CGI: Simply run php with the script that crashes it
      • +
      • PHP Apache Module: Run httpd -X, and access the script that crashes PHP
      • +
      +

    Generic way to get a core on Linux

      -
    • - Set up the core pattern (run this command as root): -
        -
      • echo "<cores dir>/core-%e.%p" > /proc/sys/kernel/core_pattern
      • -
      • make sure the directory is writable by PHP
      • -
      -
    • -
    • - Set the ulimit (see above how to do it). -
    • -
    • - Restart/rerun PHP. -
    • +
    • + Set up the core pattern (run this command as root): +
        +
      • echo "<cores dir>/core-%e.%p" > /proc/sys/kernel/core_pattern
      • +
      • make sure the directory is writable by PHP
      • +
      +
    • +
    • + Set the ulimit (see above how to do it). +
    • +
    • + Restart/rerun PHP. +

    After that any process crashing in your system, including PHP, will leave its core file in the directory you've specified in core_pattern.

    @@ -76,53 +76,53 @@

    Once you have the core file:

      -
    • - Run gdb with the path to the PHP or PHP-enabled httpd binary, and - path to the core file. Some examples: -
        -
      • gdb /usr/local/apache/sbin/httpd /usr/local/apache/sbin/core
      • -
      • gdb /home/user/dev/php-snaps/sapi/cli/php /home/user/dev/testing/core
      • -
      -
    • -
    • - At the gdb prompt, run: -
        -
      • (gdb) bt
      • -
      -
    • +
    • + Run gdb with the path to the PHP or PHP-enabled httpd binary, and + path to the core file. Some examples: +
        +
      • gdb /usr/local/apache/sbin/httpd /usr/local/apache/sbin/core
      • +
      • gdb /home/user/dev/php-snaps/sapi/cli/php /home/user/dev/testing/core
      • +
      +
    • +
    • + At the gdb prompt, run: +
        +
      • (gdb) bt
      • +
      +

    If you can't get a core file:

      -
    • - Run httpd -X under gdb with something like: -
        -
      • gdb /usr/local/apache/sbin/httpd
      • -
      • (gdb) run -X
      • -
      -
    • -
    • - Then use your web browser and access your server to force the crash. - You should see a gdb prompt appear and some message indicating that - there was a crash. At this gdb prompt, type: -
        -
      • (gdb) bt
      • -
      -
        -
      • - or, running from the commandline -
          -
        • - gdb /home/user/dev/php-snaps/sapi/cli/php -
            -
          • (gdb) run /path/to/script.php
          • -
          • (gdb) bt
          • -
          -
        • -
        -
      • -
      -
    • +
    • + Run httpd -X under gdb with something like: +
        +
      • gdb /usr/local/apache/sbin/httpd
      • +
      • (gdb) run -X
      • +
      +
    • +
    • + Then use your web browser and access your server to force the crash. + You should see a gdb prompt appear and some message indicating that + there was a crash. At this gdb prompt, type: +
        +
      • (gdb) bt
      • +
      +
        +
      • + or, running from the commandline +
          +
        • + gdb /home/user/dev/php-snaps/sapi/cli/php +
            +
          • (gdb) run /path/to/script.php
          • +
          • (gdb) bt
          • +
          +
        • +
        +
      • +
      +

    This should generate a backtrace, that you should submit in @@ -147,8 +147,8 @@ be used as a guideline on how to handle your segfault.

      -
    • Sample gdb session
    • -
      
      +    
    • Sample gdb session
    • +
      
       (gdb) bt
       #0  0x080ca21b in _efree (ptr=0xbfffdb9b) at zend_alloc.c:240
       #1  0x080d691a in _zval_dtor (zvalue=0x8186b94) at zend_variables.c:44
      @@ -172,7 +172,7 @@
       (gdb) print (char *)executor_globals.active_op_array->filename
       $16 = 0x816afbc "/home/yohgaki/php/DEV/segfault.php"
       (gdb)
      -	
      +

    In this session, frame 3 is the last execute() diff --git a/www/css/style.css b/www/css/style.css index 3f6c000f..a9b07b58 100644 --- a/www/css/style.css +++ b/www/css/style.css @@ -1,175 +1,175 @@ body { - margin: 0px; - padding: 0px; - background-color: white; - color: black; - font-family: verdana, arial, helvetica, sans-serif; - font-size: 90%; + margin: 0px; + padding: 0px; + background-color: white; + color: black; + font-family: verdana, arial, helvetica, sans-serif; + font-size: 90%; } img { - border:0 none; + border:0 none; } table.head, table.middle, table.foot { - font-size: 100%; /* make IE look normal */ - width: 100%; - border: 0px; + font-size: 100%; /* make IE look normal */ + width: 100%; + border: 0px; } td.content { - width: 100%; - vertical-align: top; - padding: 1em 0.5em 0.3em 0.5em; + width: 100%; + vertical-align: top; + padding: 1em 0.5em 0.3em 0.5em; } /* ========= Header ========= */ td.head-logo { - background-color: #5b69a6; - width: 120px; - text-align: left; + background-color: #5b69a6; + width: 120px; + text-align: left; } td.head-menu { - background-color: #5b69a6; - height: 1px; - padding-right: 0.5em; - padding-bottom: 0.1em; - text-align: right; - vertical-align: bottom; + background-color: #5b69a6; + height: 1px; + padding-right: 0.5em; + padding-bottom: 0.1em; + text-align: right; + vertical-align: bottom; } td.head-search { - color: #FFFFFF; - background-color: #9999cc; - border-top: 1px solid #000066; - border-bottom: 1px solid #000066; - padding-right: 0.5em; - text-align: right; + color: #FFFFFF; + background-color: #9999cc; + border-top: 1px solid #000066; + border-bottom: 1px solid #000066; + padding-right: 0.5em; + text-align: right; } p.head-search { - font-size: 80%; - margin-top: 0.1em; - margin-bottom: 0.1em; + font-size: 80%; + margin-top: 0.1em; + margin-bottom: 0.1em; } p.head-search input, p.head-search select { - font-size: 100%; + font-size: 100%; } td.head-menu { - color: #000; - font-weight: bold; - font-size: 75%; + color: #000; + font-weight: bold; + font-size: 75%; } td.head-menu a { - color: #FFF; - font-weight: normal; + color: #FFF; + font-weight: normal; } td.head-menu a:link, td.head-menu a:hover, td.head-menu a:visited { - text-decoration: none; + text-decoration: none; } /* ========= Footer ========= */ td.foot-bar { - color: #000000; - background-color: #5b69a6; - border-top: 1px solid #000066; - border-bottom: 1px solid #000066; - text-align: right; - vertical-align: bottom; - padding-right: 0.3em; - font-weight: bold; - font-size: 75%; + color: #000000; + background-color: #5b69a6; + border-top: 1px solid #000066; + border-bottom: 1px solid #000066; + text-align: right; + vertical-align: bottom; + padding-right: 0.3em; + font-weight: bold; + font-size: 75%; } td.foot-copy { - background-color: #CCCCCC; - vertical-align: top; - padding: 0.3em; + background-color: #CCCCCC; + vertical-align: top; + padding: 0.3em; } td.foot-source { - background-color: #CCCCCC; - vertical-align: top; - text-align: right; - padding: 0.3em; + background-color: #CCCCCC; + vertical-align: top; + text-align: right; + padding: 0.3em; } /* ========= Forms ========= */ table.form-holder { - background-color: #FFFFFF; - width: auto; + background-color: #FFFFFF; + width: auto; } /* Labels on the left side of form and table rows */ th.form-label_left { - padding: 3px; - text-align: left; - vertical-align: top; - font-weight: bold; - color: #FFFFFF; - background-color: #5b69a6; - width: 25%; + padding: 3px; + text-align: left; + vertical-align: top; + font-weight: bold; + color: #FFFFFF; + background-color: #5b69a6; + width: 25%; } td.form-input { - padding: 3px; - text-align: left; - vertical-align: top; - background-color: #e8e8e8; + padding: 3px; + text-align: left; + vertical-align: top; + background-color: #e8e8e8; } td.form-input_center { - padding: 3px; - text-align: center; - vertical-align: middle; - background-color: #e8e8e8; + padding: 3px; + text-align: center; + vertical-align: middle; + background-color: #e8e8e8; } td.form-input input, td.form-input_center input { - font-size: 90%; + font-size: 90%; } p.cell_note { - margin-top: 0.3em; - margin-bottom: 0.2em; - font-weight: normal; - font-size: 95%; + margin-top: 0.3em; + margin-bottom: 0.2em; + font-weight: normal; + font-size: 95%; } input { - font-family: verdana,arial,helvetica,sans-serif; + font-family: verdana,arial,helvetica,sans-serif; } textarea { - font-family: "andale mono", "monotype.com", "courier new", monospace; + font-family: "andale mono", "monotype.com", "courier new", monospace; } input.small, select.small { - font-size: 75%; + font-size: 75%; } textarea.small { - font-size: 75%; + font-size: 75%; } form { - margin-bottom: 0; + margin-bottom: 0; } /* === User Submission Responses === */ @@ -179,251 +179,251 @@ div.warnings, div.explain, div.success div.tags { - margin: 4px; - padding: 6px; - clear: both; - font-size: 1.1em; - font-weight: bold; + margin: 4px; + padding: 6px; + clear: both; + font-size: 1.1em; + font-weight: bold; } div.errors { - border: 1px dashed #660000; - background: #fee; - color: #660000; + border: 1px dashed #660000; + background: #fee; + color: #660000; } div.warnings { - border: 1px dashed #ff8c00; - background: #ffa; - color: #ff8c00; + border: 1px dashed #ff8c00; + background: #ffa; + color: #ff8c00; } div.tags { - border: 1px dashed #ff8c00; - background: #ffa; - color: #ff8c00; - width: 500px; - margin-left : 100px; + border: 1px dashed #ff8c00; + background: #ffa; + color: #ff8c00; + width: 500px; + margin-left : 100px; } div.explain { - border: 1px dashed #000066; - color: #000066; + border: 1px dashed #000066; + color: #000066; } div.success { - border: 1px dashed #000066; - background-color: #CCFF99; - color: #000066; - padding:10px; + border: 1px dashed #000066; + background-color: #CCFF99; + color: #000066; + padding:10px; } div.errors ul, div.warnings ul, div.explain ul, div.success ul { - margin-top: 0em; - margin-bottom: 0em; + margin-top: 0em; + margin-bottom: 0em; } /* ========= Statistics ========= */ .bug_bg0, .e { - background-color: #DDDDDD; - font-size: 85%; - text-align: center; + background-color: #DDDDDD; + font-size: 85%; + text-align: center; } .bug_bg1, .v { - background-color: #EEEEEE; - font-size: 85%; - text-align: center; + background-color: #EEEEEE; + font-size: 85%; + text-align: center; } .bug_head, .h { - background-color: #aabbcc; - color: #000000; - font-size: 80%; - font-weight: bold; - text-align: right; - padding: 0em .2em .2em .2em; + background-color: #aabbcc; + color: #000000; + font-size: 80%; + font-weight: bold; + text-align: right; + padding: 0em .2em .2em .2em; } tr.bug_header th { - background-color: #aabbcc; - color: #000000; - font-size: 80%; - font-weight: bold; - text-align: center; - padding: 0em .2em .2em .2em; + background-color: #aabbcc; + color: #000000; + font-size: 80%; + font-weight: bold; + text-align: center; + padding: 0em .2em .2em .2em; } a.bug_stats { - color: #000000; + color: #000000; } a.bug_stats_choosen { - color: #FFFF00; + color: #FFFF00; } .bug_bg_selected { - background-color: yellow; + background-color: yellow; } .bug_bg_selected a { - color: #006600; + color: #006600; } .stats-table tr:hover td { - background-color: #aabbcc; + background-color: #aabbcc; } /* ========= Other ========= */ a:link { - color: #000066; + color: #000066; } a:visited { - color: #003; + color: #003; } a:hover { - color: #000033; + color: #000033; } th.others { - color: #006600; - text-align: left; + color: #006600; + text-align: left; } td.ulcell { - vertical-align: top; + vertical-align: top; } em { - font-weight: bold; - font-style: italic; + font-weight: bold; + font-style: italic; } hr { - border: 0px; - height: 1px; - background-color: #ccc; + border: 0px; + height: 1px; + background-color: #ccc; } code, pre { - font-family: Consolas, Inconsolata, "Liberation Mono", monospace; - font-size: 13px; + font-family: Consolas, Inconsolata, "Liberation Mono", monospace; + font-size: 13px; } pre { - line-height: 1.4; + line-height: 1.4; } h1 { - font-size: 140%; - font-weight: bold; - color: #000066; - margin-top: 0em; + font-size: 140%; + font-weight: bold; + color: #000066; + margin-top: 0em; } h2 { - font-size: 125%; - font-weight: bold; - color: #000066; - border-bottom: 1px solid #000; + font-size: 125%; + font-weight: bold; + color: #000066; + border-bottom: 1px solid #000; } h3 { - font-size: 110%; - font-weight: bold; - color: #000066; + font-size: 110%; + font-weight: bold; + color: #000066; } h4 { - font-size: 100%; - font-weight: bold; - color: #000066; + font-size: 100%; + font-weight: bold; + color: #000066; } small { - font-size: 85%; + font-size: 85%; } a.small { - font-size: 75%; - text-decoration: none; + font-size: 75%; + text-decoration: none; } .indent { - margin-left: 20px; + margin-left: 20px; } .accesskey { - text-decoration: underline; + text-decoration: underline; } ul { - margin-top: 0em; + margin-top: 0em; } /* STYLES FOR THE BUG SYSTEM */ div.clear { - clear:both; + clear:both; } td.content { - font-size: 1em; + font-size: 1em; } td.content table { - font-size: 100%; /* make IE look normal */ + font-size: 100%; /* make IE look normal */ padding-bottom: 15px; } .headerbottom { - background: #9999cc; + background: #9999cc; } .note, .changeset { - width:65em; - margin-left: 10px; - white-space:pre-wrap; - font-size: 13px; + width:65em; + margin-left: 10px; + white-space:pre-wrap; + font-size: 13px; } .lusersearch .note { - width:100%; + width:100%; } .log_note { - font-family:monospace; - font-size:medium; + font-family:monospace; + font-size:medium; } .changeset { - background-color:#eee; - white-space:normal; - padding:5px; + background-color:#eee; + white-space:normal; + padding:5px; } .changeset .added { - display:block; - color: #1aa100; + display:block; + color: #1aa100; } .changeset .removed { - display:block; - color: #de0019; + display:block; + color: #de0019; } input.small, select.small { - font-size: 0.75em; + font-size: 0.75em; } textarea.small { - font-size: 0.75em; + font-size: 0.75em; } form#comment, form#update, form#vote, div.comment { - clear: both; + clear: both; padding: 15px 0; } @@ -432,240 +432,240 @@ div.comment { } table.lusersearch td { - font-size: 0.75em; + font-size: 0.75em; } table.lusersearch tr { - vertical-align: top; + vertical-align: top; } div.controls { - clear: both; - width: 100%; + clear: both; + width: 100%; } div.controls span { - display: block; - float: left; - border: 1px solid #000; - border-top: 0 none; - font-weight: bold; - background: #ddddff; - padding: 4px 8px; - margin-left: 10px; + display: block; + float: left; + border: 1px solid #000; + border-top: 0 none; + font-weight: bold; + background: #ddddff; + padding: 4px 8px; + margin-left: 10px; } div.comments span { - cursor:pointer; - font-weight:normal; + cursor:pointer; + font-weight:normal; } div#comments_view { - padding-top:10px; + padding-top:10px; } div.controls .active { - cursor:auto; - position: relative; - top: -2px; - background: #ffffff; - padding-top: 6px; + cursor:auto; + position: relative; + top: -2px; + background: #ffffff; + padding-top: 6px; } div.controls a { - text-decoration: none; + text-decoration: none; } div.controls a:hover { - text-decoration: underline; + text-decoration: underline; } td#summary { - font-weight: bold; + font-weight: bold; } div.details { - margin:5px; + margin:5px; } th.details { - vertical-align: top; - text-align: right; + vertical-align: top; + text-align: right; } th.results { - background-color: #aaaaaa; - text-align: left; + background-color: #aaaaaa; + text-align: left; } table#details td { - vertical-align: top; + vertical-align: top; } div#bugheader { - border-bottom: 2px solid #666666; - width: 100%; + border-bottom: 2px solid #666666; + width: 100%; } div#bugheader table#details th { - text-align: right; - color: #666666; + text-align: right; + color: #666666; } div#bugheader table#details th#number { - color: #000066; - font-size: larger; + color: #000066; + font-size: larger; } div#bugheader table#details td#summary { - font-weight: bold; - vertical-align:middle; + font-weight: bold; + vertical-align:middle; } .diffheader { - background: #EEEE00; + background: #EEEE00; } .newdiff { - background: #FF0000; + background: #FF0000; } .olddiff { - background: #AAAAAA; - text-decoration: line-through; + background: #AAAAAA; + text-decoration: line-through; } form#vote { - font-size: smaller; + font-size: smaller; } form#vote div.sect { - float: left; + float: left; } form#vote fieldset { - border: 1px solid #000000; + border: 1px solid #000000; } form#vote div#submit { - margin-top: 24px; - margin-left: 10px; - text-align: center; + margin-top: 24px; + margin-left: 10px; + text-align: center; } form#asearch th { - text-align: left; - font-weight: bold; + text-align: left; + font-weight: bold; } form#asearch table#primary { - font-size: 100%; /* make IE look normal */ - background: #eee; - padding: 4px; + font-size: 100%; /* make IE look normal */ + background: #eee; + padding: 4px; } table#votes { - background-color:#eee; - margin:0 0 0 50px; + background-color:#eee; + margin:0 0 0 50px; } /* === Search === */ td.search-prev_next { - background-color: #cccccc; - text-align: center; - padding-bottom: 0.4em; + background-color: #cccccc; + text-align: center; + padding-bottom: 0.4em; } td.search-prev_next table { - font-size: 100%; /* make IE look normal */ + font-size: 100%; /* make IE look normal */ } td.search-prev { - text-align: left; - width: 33%; + text-align: left; + width: 33%; } td.search-showing { - text-align: center; - width: 34%; + text-align: center; + width: 34%; } td.search-next { - text-align: right; - width: 33%; + text-align: right; + width: 33%; } .Asn { - background-color: #bbaaff; + background-color: #bbaaff; } .Ana { - background-color: #99bbaa; + background-color: #99bbaa; } .Nab { - background-color: #aaaaaa; + background-color: #aaaaaa; } .Csd { - background-color: #aaffbb; + background-color: #aaffbb; } .Ctl { - background-color: #ff0000; + background-color: #ff0000; } .Dup { - background-color: #bbbbbb; + background-color: #bbbbbb; } .Fbk { - background-color: #bbeeff; + background-color: #bbeeff; } .Fre { - background-color: #aaaaaa; + background-color: #aaaaaa; } .NoF { - background-color: #bbffcc; + background-color: #bbffcc; } .Opn { - background-color: #ffbbaa; + background-color: #ffbbaa; } .ReO { - background-color: #ffaabb; + background-color: #ffaabb; } .Sus { - background-color: #ffccbb; + background-color: #ffccbb; } .Ver { - background-color: #99bbaa; + background-color: #99bbaa; } .Wfx { - background-color: #aaaaaa; + background-color: #aaaaaa; } .Tbd { - background-color: #cdb26e; + background-color: #cdb26e; } .Sec { - background-color: #B5C7CD; + background-color: #B5C7CD; } /* {{{ Stolen from https://www.devbridge.com/sourcery/components/jquery-autocomplete/ */ .autocomplete-w1 { - background: url(http://github.com/images/shadow.png) no-repeat bottom right; - position: absolute; - top: 0px; - left: 0px; - margin: 10px 0 0 6px; - /* IE6 fix: */ - _background:none; - _margin:1px 0 0 0; + background: url(http://github.com/images/shadow.png) no-repeat bottom right; + position: absolute; + top: 0px; + left: 0px; + margin: 10px 0 0 6px; + /* IE6 fix: */ + _background:none; + _margin:1px 0 0 0; } .autocomplete { - border: 1px solid #999; - background: #FFF; - cursor: default; - text-align: left; - max-height: 350px; - overflow: auto; - margin: -6px 6px 6px -6px; - /* IE6 specific: */ - _height:350px; - _margin:0; - _overflow-x:hidden; + border: 1px solid #999; + background: #FFF; + cursor: default; + text-align: left; + max-height: 350px; + overflow: auto; + margin: -6px 6px 6px -6px; + /* IE6 specific: */ + _height:350px; + _margin:0; + _overflow-x:hidden; } .autocomplete .selected { - background:#F0F0F0; + background:#F0F0F0; } .autocomplete div { - padding: 2px 5px; - white-space: nowrap; + padding: 2px 5px; + white-space: nowrap; } .autocomplete strong { - font-weight: normal; - color: #3399FF; + font-weight: normal; + color: #3399FF; } /* }}} */ @@ -673,11 +673,11 @@ td.search-next { /* === Admin === */ td.tbl-row-message { - text-align: left; + text-align: left; } /* phpinfo() */ div.center { - margin: 0px auto; - width: 80%; + margin: 0px auto; + width: 80%; } diff --git a/www/error.php b/www/error.php index 3d703030..dd1702bd 100644 --- a/www/error.php +++ b/www/error.php @@ -10,7 +10,7 @@ // If 'id' is passed redirect to the bug page $id = !empty($_GET['id']) ? (int) $_GET['id'] : 0; if ($id) { - redirect("bug.php?id={$id}"); + redirect("bug.php?id={$id}"); } // Authenticate diff --git a/www/fix.php b/www/fix.php index 1c0d36e9..bd935b79 100644 --- a/www/fix.php +++ b/www/fix.php @@ -13,7 +13,7 @@ $bug_id = (int) $_REQUEST['id']; if (!$bug_id) { - redirect('index.php'); + redirect('index.php'); } // Authenticate @@ -24,17 +24,17 @@ $bug = $bugRepository->findOneById($bug_id); if (!is_array($bug)) { - response_header('No Such Bug'); - display_bug_error("No such bug #{$bug_id}"); - response_footer(); - exit; + response_header('No Such Bug'); + display_bug_error("No such bug #{$bug_id}"); + response_footer(); + exit; } // If bug exists, continue.. $RESOLVE_REASONS = $FIX_VARIATIONS = $errors = []; if ($logged_in != 'developer') { - $errors[] = 'The username or password you supplied was incorrect.'; + $errors[] = 'The username or password you supplied was incorrect.'; } $project = !empty($_GET['project']) ? $_GET['project'] : false; @@ -47,128 +47,128 @@ $ncomment = isset($_POST['ncomment']) ? trim($_POST['ncomment']) : ''; if (!$reason || !isset($RESOLVE_REASONS[$reason])) { - $errors[] = 'You have to use a valid reason to resolve this bug.'; + $errors[] = 'You have to use a valid reason to resolve this bug.'; } if (isset($RESOLVE_REASONS[$reason]) && $RESOLVE_REASONS[$reason]['status'] == 'Not a bug' && $ncomment == '') { - $errors[] = 'You must provide a comment when marking a bug \'Not a bug\''; + $errors[] = 'You must provide a comment when marking a bug \'Not a bug\''; } // Handle errors if ($errors) { - response_header('Error in resolving bug'); - display_bug_error($errors); + response_header('Error in resolving bug'); + display_bug_error($errors); ?>

    - + -
    - Welcome back, ! (Not ? - Log out.) -
    +
    + Welcome back, ! (Not ? + Log out.) +
    -
    - Welcome! If you don't have a Git account, you can't do anything here.
    - You can add a comment by following this link - or if you reported this bug, you can edit this bug over here. -
    - - - - - > -
    -
    +
    + Welcome! If you don't have a Git account, you can't do anything here.
    + You can add a comment by following this link + or if you reported this bug, you can edit this bug over here. +
    + + + + + > +
    +
    - - - - - - - - - -
    Reason: - -
    Note:
    - + + + + + + + + + +
    Reason: + +
    Note:
    +
    $status, - 'bug_type' => $bug['bug_type'], - 'php_version' => $bug['php_version'], - 'php_os' => $bug['php_os'], - 'assign' => $bug['assign'], + 'status' => $status, + 'bug_type' => $bug['bug_type'], + 'php_version' => $bug['php_version'], + 'php_os' => $bug['php_os'], + 'assign' => $bug['assign'], ]; // Assign automatically when closed if ($status == 'Closed' && $in['assign'] == '') { - $in['assign'] = $auth_user->handle; + $in['assign'] = $auth_user->handle; } try { - // Update bug - $dbh->prepare(" - UPDATE bugdb - SET - status = ?, - assign = ?, - ts2 = NOW() - WHERE id = ? - ")->execute([ - $status, - $in['assign'], - $bug_id, - ]); - - // Add changelog entry - $changed = bug_diff($bug, $in); - if (!empty($changed)) { - $log_comment = bug_diff_render_html($changed); - if (!empty($log_comment)) { - $result = bugs_add_comment($bug_id, $auth_user->email, $auth_user->name, $log_comment, 'log'); - } - } - - // Add possible comment - if (!empty($ncomment)) { - $result = bugs_add_comment($bug_id, $auth_user->email, $auth_user->name, $ncomment, 'comment'); - } - - // Send emails - mail_bug_updates($bug, $in, $auth_user->email, $ncomment); - redirect("bug.php?id={$bug_id}&thanks=1"); + // Update bug + $dbh->prepare(" + UPDATE bugdb + SET + status = ?, + assign = ?, + ts2 = NOW() + WHERE id = ? + ")->execute([ + $status, + $in['assign'], + $bug_id, + ]); + + // Add changelog entry + $changed = bug_diff($bug, $in); + if (!empty($changed)) { + $log_comment = bug_diff_render_html($changed); + if (!empty($log_comment)) { + $result = bugs_add_comment($bug_id, $auth_user->email, $auth_user->name, $log_comment, 'log'); + } + } + + // Add possible comment + if (!empty($ncomment)) { + $result = bugs_add_comment($bug_id, $auth_user->email, $auth_user->name, $ncomment, 'comment'); + } + + // Send emails + mail_bug_updates($bug, $in, $auth_user->email, $ncomment); + redirect("bug.php?id={$bug_id}&thanks=1"); } catch (\Exception $e) { - // If we end up here, something went wrong. - response_header('Resolve Bug: Problem'); - display_bug_error($e->getMessage()); - response_footer(); + // If we end up here, something went wrong. + response_header('Resolve Bug: Problem'); + display_bug_error($e->getMessage()); + response_footer(); } diff --git a/www/js/package-affected.js b/www/js/package-affected.js index 009afbd5..4f2e58fb 100644 --- a/www/js/package-affected.js +++ b/www/js/package-affected.js @@ -1,108 +1,108 @@ 'use strict'; window.addEventListener( - 'load', - function () { - document - .querySelectorAll('select[name="in[package_name]"]') - .forEach( - function (select) { - var packageGroup = document.createElement('select'), - initialValue = select.value, - initialGroup = select.querySelector(':scope option[value="' + initialValue + '"]').parentNode; - - packageGroup.name = 'in[package_group]'; - - select - .querySelectorAll(':scope optgroup') - .forEach( - function (optgroup) { - var packageOption = document.createElement('option'), - groupName = optgroup.label; - - packageOption.textContent = groupName; - packageOption.value = groupName; - packageGroup.appendChild(packageOption); - - optgroup - .querySelectorAll(':scope option') - .forEach( - function (option) { - option.setAttribute('bug-group', groupName); - } - ); - } - ); - - var instructions = select.querySelector(':scope option[value="none"]'); - if (instructions instanceof HTMLElement) { - instructions.textContent = 'Select a category'; - } - - select - .querySelectorAll(':scope optgroup') - .forEach( - function (optgroup) { - optgroup.style.display = 'none'; - } - ); - - function updateGroup() { - select.disabled = false; - - if (instructions instanceof HTMLElement) { - instructions.style.display = 'none'; - } - - var previousOptions = select.querySelectorAll(':scope > option:not([value=none])'), - nextLabel = packageGroup.value, - nextGroup = select.querySelector(':scope optgroup[label="' + nextLabel + '"]'); - - if (previousOptions.length !== 0) { - var previousLabel = previousOptions[0].getAttribute('bug-group'), - previousGroup = select.querySelector(':scope optgroup[label="' + previousLabel + '"]'); - - moveOptions(select, previousGroup); - } - - moveOptions(nextGroup, select); - - select.selectedIndex = -1; - } - - function moveOptions(from, to) { - from.querySelectorAll(':scope > option') - .forEach( - function (option) { - to.appendChild(option); - } - ); - } - - packageGroup.addEventListener('click', updateGroup); - packageGroup.addEventListener('change', updateGroup); - - if (initialGroup instanceof HTMLOptGroupElement) { - packageGroup.value = initialGroup.label; - moveOptions(initialGroup, select); - } else { - select.disabled = true; - select.value = null; - packageGroup.selectedIndex = -1; - } - - packageGroup.style.marginRight = '.5em'; - [select, packageGroup].forEach( - function (element) { - element.size = 12; - element.style.width = '22em'; - } - ); - - select - .parentNode - .insertBefore(packageGroup, select); - } - ); - } + 'load', + function () { + document + .querySelectorAll('select[name="in[package_name]"]') + .forEach( + function (select) { + var packageGroup = document.createElement('select'), + initialValue = select.value, + initialGroup = select.querySelector(':scope option[value="' + initialValue + '"]').parentNode; + + packageGroup.name = 'in[package_group]'; + + select + .querySelectorAll(':scope optgroup') + .forEach( + function (optgroup) { + var packageOption = document.createElement('option'), + groupName = optgroup.label; + + packageOption.textContent = groupName; + packageOption.value = groupName; + packageGroup.appendChild(packageOption); + + optgroup + .querySelectorAll(':scope option') + .forEach( + function (option) { + option.setAttribute('bug-group', groupName); + } + ); + } + ); + + var instructions = select.querySelector(':scope option[value="none"]'); + if (instructions instanceof HTMLElement) { + instructions.textContent = 'Select a category'; + } + + select + .querySelectorAll(':scope optgroup') + .forEach( + function (optgroup) { + optgroup.style.display = 'none'; + } + ); + + function updateGroup() { + select.disabled = false; + + if (instructions instanceof HTMLElement) { + instructions.style.display = 'none'; + } + + var previousOptions = select.querySelectorAll(':scope > option:not([value=none])'), + nextLabel = packageGroup.value, + nextGroup = select.querySelector(':scope optgroup[label="' + nextLabel + '"]'); + + if (previousOptions.length !== 0) { + var previousLabel = previousOptions[0].getAttribute('bug-group'), + previousGroup = select.querySelector(':scope optgroup[label="' + previousLabel + '"]'); + + moveOptions(select, previousGroup); + } + + moveOptions(nextGroup, select); + + select.selectedIndex = -1; + } + + function moveOptions(from, to) { + from.querySelectorAll(':scope > option') + .forEach( + function (option) { + to.appendChild(option); + } + ); + } + + packageGroup.addEventListener('click', updateGroup); + packageGroup.addEventListener('change', updateGroup); + + if (initialGroup instanceof HTMLOptGroupElement) { + packageGroup.value = initialGroup.label; + moveOptions(initialGroup, select); + } else { + select.disabled = true; + select.value = null; + packageGroup.selectedIndex = -1; + } + + packageGroup.style.marginRight = '.5em'; + [select, packageGroup].forEach( + function (element) { + element.size = 12; + element.style.width = '22em'; + } + ); + + select + .parentNode + .insertBefore(packageGroup, select); + } + ); + } ); diff --git a/www/js/search.js b/www/js/search.js index da9496bc..d914773e 100644 --- a/www/js/search.js +++ b/www/js/search.js @@ -1,17 +1,17 @@ var reEscape = new RegExp('(\\' + ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g'); var fnFormatSearchResult = function(value, data, currentValue) { - var pattern = '(' + currentValue.replace(reEscape, '\\$1') + ')'; - var listing = users[value]["name"] + " (" + users[value]["username"] + ")"; + var pattern = '(' + currentValue.replace(reEscape, '\\$1') + ')'; + var listing = users[value]["name"] + " (" + users[value]["username"] + ")"; - listing = listing.replace(new RegExp(pattern, 'gi'), '$1<\/strong>'); - return ' ' + listing; + listing = listing.replace(new RegExp(pattern, 'gi'), '$1<\/strong>'); + return ' ' + listing; }; $('#assigned_user').autocomplete({ - minChars:2, - maxHeight:400, - width:300, - fnFormatResult: fnFormatSearchResult, - onSelect: function(value, data){ $('#assigned_user').val(users[value]["username"]); }, - lookup: lookup + minChars:2, + maxHeight:400, + width:300, + fnFormatResult: fnFormatSearchResult, + onSelect: function(value, data){ $('#assigned_user').val(users[value]["username"]); }, + lookup: lookup }); diff --git a/www/js/userlisting.php b/www/js/userlisting.php index 139b0bf6..36c1461d 100644 --- a/www/js/userlisting.php +++ b/www/js/userlisting.php @@ -5,64 +5,64 @@ function getAllUsers() { - $opts = ['ignore_errors' => true]; - $ctx = stream_context_create(['http' => $opts]); - $token = getenv('USER_TOKEN'); + $opts = ['ignore_errors' => true]; + $ctx = stream_context_create(['http' => $opts]); + $token = getenv('USER_TOKEN'); - $retval = @file_get_contents('https://master.php.net/fetch/allusers.php?&token=' . rawurlencode($token), false, $ctx); + $retval = @file_get_contents('https://master.php.net/fetch/allusers.php?&token=' . rawurlencode($token), false, $ctx); - if (!$retval) { - return; - } + if (!$retval) { + return; + } - $json = json_decode($retval, true); + $json = json_decode($retval, true); - if (!is_array($json)) { - return; - } + if (!is_array($json)) { + return; + } - if (isset($json['error'])) { - return; - } - return $json; + if (isset($json['error'])) { + return; + } + return $json; } if (!file_exists("/tmp/svnusers.json") || filemtime("/tmp/svnusers.json") < $_SERVER["REQUEST_TIME"] - 3600) { - $json = getAllUsers(); - $json_data = var_export($json, true); - file_put_contents("/tmp/svnusers.php", ' md5($row['username'] . '@php.net'), - 'name' => $row['name'], - 'username' => $row['username'], - ]; - $user[$row["username"]] = $data; - $user[$row["name"]] = $data; - } + $data = [ + 'email' => md5($row['username'] . '@php.net'), + 'name' => $row['name'], + 'username' => $row['username'], + ]; + $user[$row["username"]] = $data; + $user[$row["name"]] = $data; + } } echo 'var users = ', json_encode($user), ";\n", - 'var lookup = ', json_encode($lookup), ";\n"; + 'var lookup = ', json_encode($lookup), ";\n"; diff --git a/www/js/util.js b/www/js/util.js index e6e9557c..d1c0fab5 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -1,10 +1,10 @@ function show(layer) { - var l = document.getElementById(layer); + var l = document.getElementById(layer); l.style.display = "block"; } function hide(layer) { - var l = document.getElementById(layer); - l.style.display = "none"; + var l = document.getElementById(layer); + l.style.display = "none"; } diff --git a/www/login.php b/www/login.php index 3aec62ad..b5f6b523 100644 --- a/www/login.php +++ b/www/login.php @@ -16,11 +16,11 @@ bugs_authenticate($user, $pwd, $logged_in, $user_flags); if ($logged_in === 'developer') { - if (!empty($_POST['referer']) && - preg_match("/^{$site_method}:\/\/". preg_quote($site_url) .'/i', $referer) && - parse_url(http://github.com/$referer,%20PHP_URL_PATH) != '/logout.php') { - redirect($referer); - } + if (!empty($_POST['referer']) && + preg_match("/^{$site_method}:\/\/". preg_quote($site_url) .'/i', $referer) && + parse_url(http://github.com/$referer,%20PHP_URL_PATH) != '/logout.php') { + redirect($referer); + } redirect('index.php'); } else { ?> @@ -28,7 +28,7 @@ diff --git a/www/lstats.php b/www/lstats.php index 765d44c8..5ed1d89a 100644 --- a/www/lstats.php +++ b/www/lstats.php @@ -6,69 +6,69 @@ function status_print ($status, $num, $width, $align = STR_PAD_LEFT) { - echo ucfirst($status), ':', str_pad($num, $width - strlen($status), ' ', $align), "\n\n"; + echo ucfirst($status), ':', str_pad($num, $width - strlen($status), ' ', $align), "\n\n"; } function get_status_count ($status, $category = '') { - global $phpver, $dbh; + global $phpver, $dbh; - $query = "SELECT count(id) from bugdb WHERE"; + $query = "SELECT count(id) from bugdb WHERE"; - if ($phpver > 0) { - $query .= " php_version LIKE '{$phpver}%' AND"; - } + if ($phpver > 0) { + $query .= " php_version LIKE '{$phpver}%' AND"; + } - /* Categories which are excluded from bug count */ - $excluded = "'Feature/Change Request', 'Systems problem', 'Website Problem', 'PEAR related', 'PECL related', 'Documentation problem', 'Translation problem', 'PHP-GTK related', 'Online Doc Editor problem'"; + /* Categories which are excluded from bug count */ + $excluded = "'Feature/Change Request', 'Systems problem', 'Website Problem', 'PEAR related', 'PECL related', 'Documentation problem', 'Translation problem', 'PHP-GTK related', 'Online Doc Editor problem'"; - if ($category != '') { - $query.= " {$status} AND bug_type = 'Bug' AND package_name = " . $dbh->quote($category); - } else { - $query.= " status='{$status}' "; - } - $query.= "AND bug_type NOT IN({$excluded})"; + if ($category != '') { + $query.= " {$status} AND bug_type = 'Bug' AND package_name = " . $dbh->quote($category); + } else { + $query.= " status='{$status}' "; + } + $query.= "AND bug_type NOT IN({$excluded})"; - $res = $dbh->prepare($query)->execute([]); - $row = $res->fetch(\PDO::FETCH_NUM); + $res = $dbh->prepare($query)->execute([]); + $row = $res->fetch(\PDO::FETCH_NUM); - return $row[0]; + return $row[0]; } // Input $phpver = (isset($_GET['phpver']) ? (int) $_GET['phpver'] : false); if (!$phpver || ($phpver !== 5 && $phpver !== 7)) { - echo "

    Bug stats for both PHP 5 and PHP 7:

    \n
    \n";
    +    echo "

    Bug stats for both PHP 5 and PHP 7:

    \n
    \n";
     } else {
    -	echo "

    Bug stats for PHP $phpver:

    \n
    \n";
    +    echo "

    Bug stats for PHP $phpver:

    \n
    \n";
     }
     
     if (isset($_GET['per_category']))
     {
    -	$packageRepository = $container->get(PackageRepository::class);
    -	$pseudo_pkgs = $packageRepository->findAll($_GET['project'] ?? '');
    -
    -	$totals = [];
    -	foreach ($pseudo_pkgs as $category => $data) {
    -		$count = get_status_count ("status NOT IN('to be documented', 'closed', 'not a bug', 'duplicate', 'wont fix', 'no feedback')", $category);
    -		if ($count > 0) {
    -			$totals[$category] = $count;
    -		}
    -	}
    -	arsort($totals);
    -	foreach ($totals as $category => $total) {
    -		status_print($category, $total, 40);
    -	}
    +    $packageRepository = $container->get(PackageRepository::class);
    +    $pseudo_pkgs = $packageRepository->findAll($_GET['project'] ?? '');
    +
    +    $totals = [];
    +    foreach ($pseudo_pkgs as $category => $data) {
    +        $count = get_status_count ("status NOT IN('to be documented', 'closed', 'not a bug', 'duplicate', 'wont fix', 'no feedback')", $category);
    +        if ($count > 0) {
    +            $totals[$category] = $count;
    +        }
    +    }
    +    arsort($totals);
    +    foreach ($totals as $category => $total) {
    +        status_print($category, $total, 40);
    +    }
     
     } else {
     
    -	foreach ($tla as $status => $short) {
    -		if (!in_array($status, ['Duplicate'])) {
    -			$count = get_status_count ($status);
    -			status_print($status, $count, 30);
    -		}
    -	}
    +    foreach ($tla as $status => $short) {
    +        if (!in_array($status, ['Duplicate'])) {
    +            $count = get_status_count ($status);
    +            status_print($status, $count, 30);
    +        }
    +    }
     
     }
     
    diff --git a/www/patch-display.php b/www/patch-display.php
    index de2b9930..6c2be54c 100644
    --- a/www/patch-display.php
    +++ b/www/patch-display.php
    @@ -19,108 +19,108 @@
     bugs_authenticate($user, $pw, $logged_in, $user_flags);
     
     if (!isset($_GET['bug_id']) && !isset($_GET['bug'])) {
    -	response_header('Error :: no bug selected');
    -	display_bug_error('No patch selected to view');
    -	response_footer();
    -	exit;
    +    response_header('Error :: no bug selected');
    +    display_bug_error('No patch selected to view');
    +    response_footer();
    +    exit;
     }
     
     $canpatch = ($logged_in == 'developer');
     
     $revision = isset($_GET['revision']) ? $_GET['revision'] : null;
    -$patch_name = isset($_GET['patch'])	? $_GET['patch'] : null;
    +$patch_name = isset($_GET['patch'])    ? $_GET['patch'] : null;
     if ($patch_name) {
    -	$patch_name_url = urlencode($patch_name);
    +    $patch_name_url = urlencode($patch_name);
     }
     
     $bug_id = !empty($_GET['bug']) ? (int) $_GET['bug'] : 0;
     if (empty($bug_id)) {
    -	$bug_id = (int) $_GET['bug_id'];
    +    $bug_id = (int) $_GET['bug_id'];
     }
     
     $bugRepository = $container->get(BugRepository::class);
     
     if (!($buginfo = $bugRepository->findOneById($bug_id))) {
    -	response_header('Error :: invalid bug selected');
    -	display_bug_error("Invalid bug #{$bug_id} selected");
    -	response_footer();
    -	exit;
    +    response_header('Error :: invalid bug selected');
    +    display_bug_error("Invalid bug #{$bug_id} selected");
    +    response_footer();
    +    exit;
     }
     
     if (!bugs_has_access($bug_id, $buginfo, $pw, $user_flags)) {
    -	response_header('Error :: No access to bug selected');
    -	display_bug_error("You have no access to bug #{$bug_id}");
    -	response_footer();
    -	exit;
    +    response_header('Error :: No access to bug selected');
    +    display_bug_error("You have no access to bug #{$bug_id}");
    +    response_footer();
    +    exit;
     }
     
     if (isset($patch_name) && isset($revision)) {
    -	if ($revision == 'latest') {
    -		$revisions = $patchRepository->findRevisions($buginfo['id'], $patch_name);
    -		if (isset($revisions[0])) {
    -			$revision = $revisions[0]['revision'];
    -		}
    -	}
    -
    -	$path = $patchTracker->getPatchFullpath($bug_id, $patch_name, $revision);
    -	if (!file_exists($path)) {
    -		response_header('Error :: no such patch/revision');
    -		display_bug_error('Invalid patch/revision specified');
    -		response_footer();
    -		exit;
    -	}
    -
    -	if (isset($_GET['download'])) {
    -		header('Last-modified: ' . gmdate('l, d-M-y H:i:s \G\M\T', filemtime($path)));
    -		header('Content-type: application/octet-stream');
    -		header('Content-disposition: attachment; filename="' . $patch_name . '.patch.txt"');
    -		header('Content-length: '.filesize($path));
    -		readfile($path);
    -		exit;
    -	}
    -
    -	try {
    -		$patchcontents = $patchRepository->getPatchContents($buginfo['id'], $patch_name, $revision);
    -	} catch (\Exception $e) {
    -		response_header('Error :: Cannot retrieve patch');
    -		display_bug_error('Internal error: Invalid patch/revision specified (is in database, but not in filesystem)');
    -		response_footer();
    -		exit;
    -	}
    -
    -	$package_name = $buginfo['package_name'];
    -	$handle = $patchRepository->findDeveloper($bug_id, $patch_name, $revision);
    -	$obsoletedby = $obsoletePatchRepository->findObsoletingPatches($bug_id, $patch_name, $revision);
    -	$obsoletes = $obsoletePatchRepository->findObsoletePatches($bug_id, $patch_name, $revision);
    -	$patches = $patchRepository->findAllByBugId($bug_id);
    -	$revisions = $patchRepository->findRevisions($bug_id, $patch_name);
    -
    -	response_header("Bug #{$bug_id} :: Patches");
    -	include "{$ROOT_DIR}/templates/listpatches.php";
    -
    -	if (isset($_GET['diff']) && $_GET['diff'] && isset($_GET['old']) && is_numeric($_GET['old'])) {
    -		$old = $patchTracker->getPatchFullpath($bug_id, $patch_name, $_GET['old']);
    -		$new = $path;
    -		if (!realpath($old) || !realpath($new)) {
    -			response_header('Error :: Cannot retrieve patch');
    -			display_bug_error('Internal error: Invalid patch revision specified for diff');
    -			response_footer();
    -			exit;
    -		}
    -
    -		assert_options(ASSERT_WARNING, 0);
    -
    -		$d = new \Horde_Text_Diff('auto', [file($old), file($new)]);
    -		$diff = new Diff($d);
    -
    -		include "{$ROOT_DIR}/templates/patchdiff.php";
    -
    -		response_footer();
    -		exit;
    -	}
    -	include "{$ROOT_DIR}/templates/patchdisplay.php";
    -	response_footer();
    -	exit;
    +    if ($revision == 'latest') {
    +        $revisions = $patchRepository->findRevisions($buginfo['id'], $patch_name);
    +        if (isset($revisions[0])) {
    +            $revision = $revisions[0]['revision'];
    +        }
    +    }
    +
    +    $path = $patchTracker->getPatchFullpath($bug_id, $patch_name, $revision);
    +    if (!file_exists($path)) {
    +        response_header('Error :: no such patch/revision');
    +        display_bug_error('Invalid patch/revision specified');
    +        response_footer();
    +        exit;
    +    }
    +
    +    if (isset($_GET['download'])) {
    +        header('Last-modified: ' . gmdate('l, d-M-y H:i:s \G\M\T', filemtime($path)));
    +        header('Content-type: application/octet-stream');
    +        header('Content-disposition: attachment; filename="' . $patch_name . '.patch.txt"');
    +        header('Content-length: '.filesize($path));
    +        readfile($path);
    +        exit;
    +    }
    +
    +    try {
    +        $patchcontents = $patchRepository->getPatchContents($buginfo['id'], $patch_name, $revision);
    +    } catch (\Exception $e) {
    +        response_header('Error :: Cannot retrieve patch');
    +        display_bug_error('Internal error: Invalid patch/revision specified (is in database, but not in filesystem)');
    +        response_footer();
    +        exit;
    +    }
    +
    +    $package_name = $buginfo['package_name'];
    +    $handle = $patchRepository->findDeveloper($bug_id, $patch_name, $revision);
    +    $obsoletedby = $obsoletePatchRepository->findObsoletingPatches($bug_id, $patch_name, $revision);
    +    $obsoletes = $obsoletePatchRepository->findObsoletePatches($bug_id, $patch_name, $revision);
    +    $patches = $patchRepository->findAllByBugId($bug_id);
    +    $revisions = $patchRepository->findRevisions($bug_id, $patch_name);
    +
    +    response_header("Bug #{$bug_id} :: Patches");
    +    include "{$ROOT_DIR}/templates/listpatches.php";
    +
    +    if (isset($_GET['diff']) && $_GET['diff'] && isset($_GET['old']) && is_numeric($_GET['old'])) {
    +        $old = $patchTracker->getPatchFullpath($bug_id, $patch_name, $_GET['old']);
    +        $new = $path;
    +        if (!realpath($old) || !realpath($new)) {
    +            response_header('Error :: Cannot retrieve patch');
    +            display_bug_error('Internal error: Invalid patch revision specified for diff');
    +            response_footer();
    +            exit;
    +        }
    +
    +        assert_options(ASSERT_WARNING, 0);
    +
    +        $d = new \Horde_Text_Diff('auto', [file($old), file($new)]);
    +        $diff = new Diff($d);
    +
    +        include "{$ROOT_DIR}/templates/patchdiff.php";
    +
    +        response_footer();
    +        exit;
    +    }
    +    include "{$ROOT_DIR}/templates/patchdisplay.php";
    +    response_footer();
    +    exit;
     }
     
     $patches = $patchTracker->listPatches($bug_id);
    diff --git a/www/quick-fix-desc.php b/www/quick-fix-desc.php
    index 208f7711..c360379d 100644
    --- a/www/quick-fix-desc.php
    +++ b/www/quick-fix-desc.php
    @@ -20,26 +20,26 @@
      $reason) {
    -	if (!empty($reason['package_name']))
    -		$reason['title'] = "{$reason['title']} ({$reason['package_name']})";
    -
    -	echo "
    -		
    -			{$reason['title']}
    -			Status: {$reason['status']}
    -			
    {$reason['message']}
    - - "; + if (!empty($reason['package_name'])) + $reason['title'] = "{$reason['title']} ({$reason['package_name']})"; + + echo " + + {$reason['title']} + Status: {$reason['status']} +
    {$reason['message']}
    + + "; if (isset($FIX_VARIATIONS[$key])) { - foreach ($FIX_VARIATIONS[$key] as $type => $variation) { - echo " - - {$reason['title']} ({$type}) - Status: {$reason['status']} -
    {$variation}
    - "; - } - } + foreach ($FIX_VARIATIONS[$key] as $type => $variation) { + echo " + + {$reason['title']} ({$type}) + Status: {$reason['status']} +
    {$variation}
    + "; + } + } } ?> diff --git a/www/report.php b/www/report.php index dc556097..ecc4a1e5 100644 --- a/www/report.php +++ b/www/report.php @@ -32,223 +32,223 @@ // captcha is not necessary if the user is logged in if (!$logged_in) { - $captcha = $container->get(Captcha::class); + $captcha = $container->get(Captcha::class); } $packageAffectedScript = << + SCRIPT; // Handle input if (isset($_POST['in'])) { - $errors = incoming_details_are_valid($_POST['in'], 1, $logged_in); + $errors = incoming_details_are_valid($_POST['in'], 1, $logged_in); - // Check if session answer is set, then compare it with the post captcha value. - // If it's not the same, then it's an incorrect password. - if (!$logged_in) { - if (!isset($_SESSION['answer'])) { - $errors[] = 'Please enable cookies so the Captcha system can work'; - } elseif ($_POST['captcha'] != $_SESSION['answer']) { - $errors[] = 'Incorrect Captcha'; - } - if (is_spam($_POST['in']['ldesc']) || - is_spam($_POST['in']['expres']) || - is_spam($_POST['in']['repcode'])) { - $errors[] = 'Spam detected'; - } - } + // Check if session answer is set, then compare it with the post captcha value. + // If it's not the same, then it's an incorrect password. + if (!$logged_in) { + if (!isset($_SESSION['answer'])) { + $errors[] = 'Please enable cookies so the Captcha system can work'; + } elseif ($_POST['captcha'] != $_SESSION['answer']) { + $errors[] = 'Incorrect Captcha'; + } + if (is_spam($_POST['in']['ldesc']) || + is_spam($_POST['in']['expres']) || + is_spam($_POST['in']['repcode'])) { + $errors[] = 'Spam detected'; + } + } - // Set auto-generated password when not supplied or logged in - if ($logged_in || $_POST['in']['passwd'] == '') { - $_POST['in']['passwd'] = uniqid(); - } + // Set auto-generated password when not supplied or logged in + if ($logged_in || $_POST['in']['passwd'] == '') { + $_POST['in']['passwd'] = uniqid(); + } - // try to verify the user - $_POST['in']['email'] = $auth_user->email; + // try to verify the user + $_POST['in']['email'] = $auth_user->email; - $package_name = $_POST['in']['package_name']; + $package_name = $_POST['in']['package_name']; - if (!$errors) { - // When user submits a report, do a search and display the results before allowing them to continue. - if (!isset($_POST['preview']) && empty($_POST['in']['did_luser_search'])) { + if (!$errors) { + // When user submits a report, do a search and display the results before allowing them to continue. + if (!isset($_POST['preview']) && empty($_POST['in']['did_luser_search'])) { - $_POST['in']['did_luser_search'] = 1; + $_POST['in']['did_luser_search'] = 1; - $where_clause = "WHERE package_name != 'Feature/Change Request'"; + $where_clause = "WHERE package_name != 'Feature/Change Request'"; - if (!($user_flags & BUGS_SECURITY_DEV)) { - $where_clause .= " AND private = 'N' "; - } + if (!($user_flags & BUGS_SECURITY_DEV)) { + $where_clause .= " AND private = 'N' "; + } - // search for a match using keywords from the subject - list($sql_search, $ignored) = format_search_string($_POST['in']['sdesc']); + // search for a match using keywords from the subject + list($sql_search, $ignored) = format_search_string($_POST['in']['sdesc']); - $where_clause .= $sql_search; + $where_clause .= $sql_search; - $query = "SELECT * from bugdb $where_clause LIMIT 5"; + $query = "SELECT * from bugdb $where_clause LIMIT 5"; - $possible_duplicates = $dbh->prepare($query)->execute()->fetchAll(); + $possible_duplicates = $dbh->prepare($query)->execute()->fetchAll(); - if (!$possible_duplicates) { - $ok_to_submit_report = true; - } else { - response_header("Report - Confirm", $packageAffectedScript); - if (count($_FILES)) { - echo '

    WARNING: YOU MUST RE-UPLOAD YOUR PATCH, OR IT WILL BE IGNORED

    '; - } + if (!$possible_duplicates) { + $ok_to_submit_report = true; + } else { + response_header("Report - Confirm", $packageAffectedScript); + if (count($_FILES)) { + echo '

    WARNING: YOU MUST RE-UPLOAD YOUR PATCH, OR IT WILL BE IGNORED

    '; + } ?> -

    - Are you sure that you searched before you submitted your bug report? We - found the following bugs that seem to be similar to yours; please check - them before submitting the report as they might contain the solution you - are looking for. -

    - -

    - If you're sure that your report is a genuine bug that has not been reported - before, you can scroll down and click the "Send Bug Report" button again to - really enter the details into our database. -

    - -
    - - - - - +

    + Are you sure that you searched before you submitted your bug report? We + found the following bugs that seem to be similar to yours; please check + them before submitting the report as they might contain the solution you + are looking for. +

    + +

    + If you're sure that your report is a genuine bug that has not been reported + before, you can scroll down and click the "Send Bug Report" button again to + really enter the details into our database. +

    + +
    +
    DescriptionPossible Solution
    + + + + prepare(" - SELECT comment - FROM bugdb_comments - WHERE bug = ? - ORDER BY id DESC - LIMIT 1 - ")->execute([$row['id']])->fetch(\PDO::FETCH_NUM)[0]; - - $summary = $row['ldesc']; - if (strlen($summary) > 256) { - $summary = substr(trim($summary), 0, 256) . ' ...'; - } - - $bug_url = "bug.php?id={$row['id']}"; - - $sdesc = htmlspecialchars($row['sdesc']); - $summary = htmlspecialchars($summary); - $resolution = htmlspecialchars($resolution); - - echo <<< OUTPUT - - - - - - - + foreach ($possible_duplicates as $row) { + $resolution = $dbh->prepare(" + SELECT comment + FROM bugdb_comments + WHERE bug = ? + ORDER BY id DESC + LIMIT 1 + ")->execute([$row['id']])->fetch(\PDO::FETCH_NUM)[0]; + + $summary = $row['ldesc']; + if (strlen($summary) > 256) { + $summary = substr(trim($summary), 0, 256) . ' ...'; + } + + $bug_url = "bug.php?id={$row['id']}"; + + $sdesc = htmlspecialchars($row['sdesc']); + $summary = htmlspecialchars($summary); + $resolution = htmlspecialchars($resolution); + + echo <<< OUTPUT + + + + + + + OUTPUT; - } - - echo " -
    DescriptionPossible Solution
    {$row['package_name']} : Bug #{$row['id']}: {$sdesc}
    {$summary}
    {$resolution}
    {$row['package_name']} : Bug #{$row['id']}: {$sdesc}
    {$summary}
    {$resolution}
    -
    - "; - } - } else { - // We displayed the luser search and they said it really was not already submitted, so let's allow them to submit. - $ok_to_submit_report = true; - } - - if (isset($_POST['edit_after_preview'])) { - $ok_to_submit_report = false; - response_header("Report - New", $packageAffectedScript); - } - - if ($ok_to_submit_report) { - $_POST['in']['reporter_name'] = $auth_user->name; - $_POST['in']['handle'] = $auth_user->handle; - - // Put all text areas together. - $fdesc = "Description:\n------------\n" . $_POST['in']['ldesc'] . "\n\n"; - if (!empty($_POST['in']['repcode'])) { - $fdesc .= "Test script:\n---------------\n"; - $fdesc .= $_POST['in']['repcode'] . "\n\n"; - } - if (!empty($_POST['in']['expres']) || $_POST['in']['expres'] === '0') { - $fdesc .= "Expected result:\n----------------\n"; - $fdesc .= $_POST['in']['expres'] . "\n\n"; - } - if (!empty($_POST['in']['actres']) || $_POST['in']['actres'] === '0') { - $fdesc .= "Actual result:\n--------------\n"; - $fdesc .= $_POST['in']['actres'] . "\n"; - } - - // Bug type 'Security' marks automatically the report as private - $_POST['in']['private'] = ($_POST['in']['bug_type'] == 'Security') ? 'Y' : 'N'; - $_POST['in']['block_user_comment'] = 'N'; - - if (isset($_POST['preview'])) { - $_POST['in']['status'] = 'Open'; - $_SESSION['bug_preview'] = $_POST['in']; - $_SESSION['bug_preview']['ldesc_orig'] = $_POST['in']['ldesc']; - $_SESSION['bug_preview']['ldesc'] = $fdesc; - $_SESSION['captcha'] = $_POST['captcha']; - redirect('bug.php?id=preview'); - } - - $res = $dbh->prepare(' - INSERT INTO bugdb ( - package_name, - bug_type, - email, - sdesc, - ldesc, - php_version, - php_os, - passwd, - reporter_name, - status, - ts1, - private, - visitor_ip - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, "Open", NOW(), ?, INET6_ATON(?)) - ')->execute([ - $package_name, - $_POST['in']['bug_type'], - $_POST['in']['email'], - $_POST['in']['sdesc'], - $fdesc, - $_POST['in']['php_version'], - $_POST['in']['php_os'], - bugs_get_hash($_POST['in']['passwd']), - $_POST['in']['reporter_name'], - $_POST['in']['private'], - $_SERVER['REMOTE_ADDR'] - ] - ); - - $cid = $dbh->lastInsertId(); - - $redirectToPatchAdd = false; - if (!empty($_POST['in']['patchname']) && $_POST['in']['patchname']) { - $tracker = $container->get(PatchTracker::class); - - try { - $developer = !empty($_POST['in']['handle']) ? $_POST['in']['handle'] : $_POST['in']['email']; - $patchrevision = $tracker->attach($cid, 'patchfile', $_POST['in']['patchname'], $developer, []); - } catch (\Exception $e) { - $redirectToPatchAdd = true; - } - } - - if (empty($_POST['in']['handle'])) { - $mailfrom = spam_protect($_POST['in']['email'], 'text'); - } else { - $mailfrom = $_POST['in']['handle']; - } - - $report = <<< REPORT + } + + echo " + + + "; + } + } else { + // We displayed the luser search and they said it really was not already submitted, so let's allow them to submit. + $ok_to_submit_report = true; + } + + if (isset($_POST['edit_after_preview'])) { + $ok_to_submit_report = false; + response_header("Report - New", $packageAffectedScript); + } + + if ($ok_to_submit_report) { + $_POST['in']['reporter_name'] = $auth_user->name; + $_POST['in']['handle'] = $auth_user->handle; + + // Put all text areas together. + $fdesc = "Description:\n------------\n" . $_POST['in']['ldesc'] . "\n\n"; + if (!empty($_POST['in']['repcode'])) { + $fdesc .= "Test script:\n---------------\n"; + $fdesc .= $_POST['in']['repcode'] . "\n\n"; + } + if (!empty($_POST['in']['expres']) || $_POST['in']['expres'] === '0') { + $fdesc .= "Expected result:\n----------------\n"; + $fdesc .= $_POST['in']['expres'] . "\n\n"; + } + if (!empty($_POST['in']['actres']) || $_POST['in']['actres'] === '0') { + $fdesc .= "Actual result:\n--------------\n"; + $fdesc .= $_POST['in']['actres'] . "\n"; + } + + // Bug type 'Security' marks automatically the report as private + $_POST['in']['private'] = ($_POST['in']['bug_type'] == 'Security') ? 'Y' : 'N'; + $_POST['in']['block_user_comment'] = 'N'; + + if (isset($_POST['preview'])) { + $_POST['in']['status'] = 'Open'; + $_SESSION['bug_preview'] = $_POST['in']; + $_SESSION['bug_preview']['ldesc_orig'] = $_POST['in']['ldesc']; + $_SESSION['bug_preview']['ldesc'] = $fdesc; + $_SESSION['captcha'] = $_POST['captcha']; + redirect('bug.php?id=preview'); + } + + $res = $dbh->prepare(' + INSERT INTO bugdb ( + package_name, + bug_type, + email, + sdesc, + ldesc, + php_version, + php_os, + passwd, + reporter_name, + status, + ts1, + private, + visitor_ip + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, "Open", NOW(), ?, INET6_ATON(?)) + ')->execute([ + $package_name, + $_POST['in']['bug_type'], + $_POST['in']['email'], + $_POST['in']['sdesc'], + $fdesc, + $_POST['in']['php_version'], + $_POST['in']['php_os'], + bugs_get_hash($_POST['in']['passwd']), + $_POST['in']['reporter_name'], + $_POST['in']['private'], + $_SERVER['REMOTE_ADDR'] + ] + ); + + $cid = $dbh->lastInsertId(); + + $redirectToPatchAdd = false; + if (!empty($_POST['in']['patchname']) && $_POST['in']['patchname']) { + $tracker = $container->get(PatchTracker::class); + + try { + $developer = !empty($_POST['in']['handle']) ? $_POST['in']['handle'] : $_POST['in']['email']; + $patchrevision = $tracker->attach($cid, 'patchfile', $_POST['in']['patchname'], $developer, []); + } catch (\Exception $e) { + $redirectToPatchAdd = true; + } + } + + if (empty($_POST['in']['handle'])) { + $mailfrom = spam_protect($_POST['in']['email'], 'text'); + } else { + $mailfrom = $_POST['in']['handle']; + } + + $report = <<< REPORT From: {$mailfrom} Operating system: {$_POST['in']['php_os']} PHP version: {$_POST['in']['php_version']} @@ -257,133 +257,133 @@ Bug description: REPORT; - $ascii_report = "{$report}{$_POST['in']['sdesc']}\n\n" . wordwrap($fdesc, 72); - $ascii_report.= "\n-- \nEdit bug report at "; - $ascii_report.= "{$site_method}://{$site_url}{$basedir}/bug.php?id=$cid&edit="; - - list($mailto, $mailfrom, $bcc, $params) = get_package_mail($package_name, false, $_POST['in']['bug_type']); - - $protected_email = '"' . spam_protect($_POST['in']['email'], 'text') . '"' . "<{$mailfrom}>"; - - $extra_headers = "From: {$protected_email}\n"; - $extra_headers.= "X-PHP-BugTracker: {$siteBig}bug\n"; - $extra_headers.= "X-PHP-Bug: {$cid}\n"; - $extra_headers.= "X-PHP-Type: {$_POST['in']['bug_type']}\n"; - $extra_headers.= "X-PHP-Version: {$_POST['in']['php_version']}\n"; - $extra_headers.= "X-PHP-Category: {$package_name}\n"; - $extra_headers.= "X-PHP-OS: {$_POST['in']['php_os']}\n"; - $extra_headers.= "X-PHP-Status: Open\n"; - $extra_headers.= "Message-ID: "; - - if (isset($bug_types[$_POST['in']['bug_type']])) { - $type = $bug_types[$_POST['in']['bug_type']]; - } else { - $type = 'unknown'; - } - - // provide shortcut URLS for "quick bug fixes" - $reasonRepository = $container->get(ReasonRepository::class); - list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($_GET['project'] ?? ''); - - $dev_extra = ''; - $maxkeysize = 0; - foreach ($RESOLVE_REASONS as $v) { - if (!$v['webonly']) { - $actkeysize = strlen($v['title']) + 1; - $maxkeysize = (($maxkeysize < $actkeysize) ? $actkeysize : $maxkeysize); - } - } - foreach ($RESOLVE_REASONS as $k => $v) { - if (!$v['webonly']) { - $dev_extra .= str_pad("{$v['title']}:", $maxkeysize) . " {$site_method}://{$site_url}/fix.php?id={$cid}&r={$k}\n"; - } - } - - // mail to reporter - bugs_mail( - $_POST['in']['email'], - "$type #$cid: {$_POST['in']['sdesc']}", - "{$ascii_report}2\n", - "From: $siteBig Bug Database <$mailfrom>\n" . - "X-PHP-Bug: $cid\n" . - "X-PHP-Site: {$siteBig}\n" . - "Message-ID: " - ); - - // mail to package mailing list - bugs_mail( - $mailto, - "[$siteBig-BUG] $type #$cid [NEW]: {$_POST['in']['sdesc']}", - $ascii_report . "1\n-- \n{$dev_extra}", - $extra_headers, - $params - ); - - if ($redirectToPatchAdd) { - $patchname = urlencode($_POST['in']['patchname']); - $patchemail= urlencode($_POST['in']['email']); - redirect("patch-add.php?bug_id={$cid}&patchname={$patchname}&email={$patchemail}"); - } - redirect("bug.php?id={$cid}&thanks=4"); - } - } else { - // had errors... - response_header('Report - Problems', $packageAffectedScript); - } + $ascii_report = "{$report}{$_POST['in']['sdesc']}\n\n" . wordwrap($fdesc, 72); + $ascii_report.= "\n-- \nEdit bug report at "; + $ascii_report.= "{$site_method}://{$site_url}{$basedir}/bug.php?id=$cid&edit="; + + list($mailto, $mailfrom, $bcc, $params) = get_package_mail($package_name, false, $_POST['in']['bug_type']); + + $protected_email = '"' . spam_protect($_POST['in']['email'], 'text') . '"' . "<{$mailfrom}>"; + + $extra_headers = "From: {$protected_email}\n"; + $extra_headers.= "X-PHP-BugTracker: {$siteBig}bug\n"; + $extra_headers.= "X-PHP-Bug: {$cid}\n"; + $extra_headers.= "X-PHP-Type: {$_POST['in']['bug_type']}\n"; + $extra_headers.= "X-PHP-Version: {$_POST['in']['php_version']}\n"; + $extra_headers.= "X-PHP-Category: {$package_name}\n"; + $extra_headers.= "X-PHP-OS: {$_POST['in']['php_os']}\n"; + $extra_headers.= "X-PHP-Status: Open\n"; + $extra_headers.= "Message-ID: "; + + if (isset($bug_types[$_POST['in']['bug_type']])) { + $type = $bug_types[$_POST['in']['bug_type']]; + } else { + $type = 'unknown'; + } + + // provide shortcut URLS for "quick bug fixes" + $reasonRepository = $container->get(ReasonRepository::class); + list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($_GET['project'] ?? ''); + + $dev_extra = ''; + $maxkeysize = 0; + foreach ($RESOLVE_REASONS as $v) { + if (!$v['webonly']) { + $actkeysize = strlen($v['title']) + 1; + $maxkeysize = (($maxkeysize < $actkeysize) ? $actkeysize : $maxkeysize); + } + } + foreach ($RESOLVE_REASONS as $k => $v) { + if (!$v['webonly']) { + $dev_extra .= str_pad("{$v['title']}:", $maxkeysize) . " {$site_method}://{$site_url}/fix.php?id={$cid}&r={$k}\n"; + } + } + + // mail to reporter + bugs_mail( + $_POST['in']['email'], + "$type #$cid: {$_POST['in']['sdesc']}", + "{$ascii_report}2\n", + "From: $siteBig Bug Database <$mailfrom>\n" . + "X-PHP-Bug: $cid\n" . + "X-PHP-Site: {$siteBig}\n" . + "Message-ID: " + ); + + // mail to package mailing list + bugs_mail( + $mailto, + "[$siteBig-BUG] $type #$cid [NEW]: {$_POST['in']['sdesc']}", + $ascii_report . "1\n-- \n{$dev_extra}", + $extra_headers, + $params + ); + + if ($redirectToPatchAdd) { + $patchname = urlencode($_POST['in']['patchname']); + $patchemail= urlencode($_POST['in']['email']); + redirect("patch-add.php?bug_id={$cid}&patchname={$patchname}&email={$patchemail}"); + } + redirect("bug.php?id={$cid}&thanks=4"); + } + } else { + // had errors... + response_header('Report - Problems', $packageAffectedScript); + } } // end of if input $package = !empty($_REQUEST['package']) ? $_REQUEST['package'] : (!empty($package_name) ? $package_name : (isset($_POST['in']) && $_POST['in'] && isset($_POST['in']['package_name']) ? $_POST['in']['package_name'] : '')); if (!is_string($package)) { - response_header('Report - Problems', $packageAffectedScript); - $errors[] = 'Invalid package name passed. Please fix it and try again.'; - display_bug_error($errors); - response_footer(); - exit; + response_header('Report - Problems', $packageAffectedScript); + $errors[] = 'Invalid package name passed. Please fix it and try again.'; + display_bug_error($errors); + response_footer(); + exit; } if (!isset($_POST['in'])) { - $_POST['in'] = [ - 'package_name' => isset($_GET['package_name']) ? clean($_GET['package_name']) : '', - 'bug_type' => isset($_GET['bug_type']) ? clean($_GET['bug_type']) : '', - 'email' => '', - 'sdesc' => '', - 'ldesc' => isset($_GET['manpage']) ? clean("\n---\nFrom manual page: https://php.net/" . ltrim($_GET['manpage'], '/') . "\n---\n") : '', - 'repcode' => '', - 'expres' => '', - 'actres' => '', - 'php_version' => '', - 'php_os' => '', - 'passwd' => '', - ]; - - - response_header('Report - New', $packageAffectedScript); + $_POST['in'] = [ + 'package_name' => isset($_GET['package_name']) ? clean($_GET['package_name']) : '', + 'bug_type' => isset($_GET['bug_type']) ? clean($_GET['bug_type']) : '', + 'email' => '', + 'sdesc' => '', + 'ldesc' => isset($_GET['manpage']) ? clean("\n---\nFrom manual page: https://php.net/" . ltrim($_GET['manpage'], '/') . "\n---\n") : '', + 'repcode' => '', + 'expres' => '', + 'actres' => '', + 'php_version' => '', + 'php_os' => '', + 'passwd' => '', + ]; + + + response_header('Report - New', $packageAffectedScript); ?> -

    - Before you report a bug, make sure to search for similar bugs using the "Bug List" link. - Also, read the instructions for how to report a bug that someone will want to help fix. -

    +

    + Before you report a bug, make sure to search for similar bugs using the "Bug List" link. + Also, read the instructions for how to report a bug that someone will want to help fix. +

    -

    - If you aren't sure that what you're about to report is a bug, you should ask for help using one of the means for support - listed here. -

    +

    + If you aren't sure that what you're about to report is a bug, you should ask for help using one of the means for support + listed here. +

    -

    - Failure to follow these instructions may result in your bug simply being marked as "not a bug." -

    +

    + Failure to follow these instructions may result in your bug simply being marked as "not a bug." +

    -

    Report PEAR related bugs here

    +

    Report PEAR related bugs here

    -

    - If you feel this bug concerns a security issue, e.g. a buffer overflow, weak encryption, etc, then email +

    + If you feel this bug concerns a security issue, e.g. a buffer overflow, weak encryption, etc, then email - - who will assess the situation or use Security as bug type in the form below. -

    + + who will assess the situation or use Security as bug type in the form below. +

    -
    - - + + +
    - - - - + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + getAnswer(); + $_SESSION['answer'] = $captcha->getAnswer(); - if (!empty($_POST['captcha']) && empty($ok_to_submit_report)) { - $captcha_label = 'Solve this new problem:'; - } else { - $captcha_label = 'Solve the problem:'; - } + if (!empty($_POST['captcha']) && empty($ok_to_submit_report)) { + $captcha_label = 'Solve this new problem:'; + } else { + $captcha_label = 'Solve the problem:'; + } ?> - - - - + + + + - - - - -
    Your handle: - handle; ?> - -
    Your handle: + handle; ?> + +
    Your email address:
    MUST BE VALID
    - -
    Password: -
    - You must enter any password here, which will be stored for this bug report.
    - This password allows you to come back and modify your submitted bug report at a later date. - [Lost a bug password?] -
    Your email address:
    MUST BE VALID
    + +
    Password: +
    + You must enter any password here, which will be stored for this bug report.
    + This password allows you to come back and modify your submitted bug report at a later date. + [Lost a bug password?] +
    PHP version: - -
    Package affected: - -
    Bug Type: - -
    Operating system: - -
    Summary: - -
    Note: - Please supply any information that may be helpful in fixing the bug: -
      -
    • The version number of the package or files you are using.
    • -
    • A short script that reproduces the problem.
    • -
    • The list of modules you compiled PHP with (your configure line).
    • -
    • Any other information unique or specific to your setup.
    • -
    • Any changes made in your php.ini compared to php.ini-dist or php.ini-recommended (not your whole php.ini!)
    • -
    • A gdb backtrace.
    • -
    -
    - Description: -

    - Put short code samples in the "Test script" section below - and upload patches below. -

    -
    - -
    - Test script: -

    - A short test script you wrote that demonstrates the bug. - Please do not post more than 20 lines of code. - If the code is longer than 20 lines, provide a URL to the source - code that will reproduce the bug. -

    -
    - -
    PHP version: + +
    Package affected: + +
    Bug Type: + +
    Operating system: + +
    Summary: + +
    Note: + Please supply any information that may be helpful in fixing the bug: +
      +
    • The version number of the package or files you are using.
    • +
    • A short script that reproduces the problem.
    • +
    • The list of modules you compiled PHP with (your configure line).
    • +
    • Any other information unique or specific to your setup.
    • +
    • Any changes made in your php.ini compared to php.ini-dist or php.ini-recommended (not your whole php.ini!)
    • +
    • A gdb backtrace.
    • +
    +
    + Description: +

    + Put short code samples in the "Test script" section below + and upload patches below. +

    +
    + +
    + Test script: +

    + A short test script you wrote that demonstrates the bug. + Please do not post more than 20 lines of code. + If the code is longer than 20 lines, provide a URL to the source + code that will reproduce the bug. +

    +
    + +
    - Expected result: -

    - Skip if irrelevant. - What do you expect to happen or see when you run the test script above? -

    -
    - -
    - Actual result: -

    - Skip if irrelevant. - This could be a backtrace for example. - Try to keep it as short as possible without leaving anything relevant out. -

    -
    - -
    + Expected result: +

    + Skip if irrelevant. + What do you expect to happen or see when you run the test script above? +

    +
    + +
    + Actual result: +

    + Skip if irrelevant. + This could be a backtrace for example. + Try to keep it as short as possible without leaving anything relevant out. +

    +
    + +

    getQuestion()); ?>

    getQuestion()); ?>
    Submit: - - -
    -
    + + Submit: + + + + + + + ['error' => 'Missing bug id']]); - exit; + echo json_encode(['result' => ['error' => 'Missing bug id']]); + exit; } // Obtain common includes require_once '../include/prepend.php'; if (isset($_POST['MAGIC_COOKIE'])) { - list($user, $pwd) = explode(":", base64_decode($_POST['MAGIC_COOKIE']), 2); - $auth_user = new stdClass; - $auth_user->handle = $user; - $auth_user->password = $pwd; + list($user, $pwd) = explode(":", base64_decode($_POST['MAGIC_COOKIE']), 2); + $auth_user = new stdClass; + $auth_user->handle = $user; + $auth_user->password = $pwd; } else { - echo json_encode(['result' => ['error' => 'Missing credentials']]); - exit; + echo json_encode(['result' => ['error' => 'Missing credentials']]); + exit; } bugs_authenticate($user, $pwd, $logged_in, $user_flags); if (empty($auth_user->handle)) { - echo json_encode(['result' => ['error' => 'Invalid user or password']]); - exit; + echo json_encode(['result' => ['error' => 'Invalid user or password']]); + exit; } // fetch info about the bug into $bug @@ -41,57 +41,57 @@ $bug = $bugRepository->findOneById($bug_id); if (!is_array($bug)) { - echo json_encode(['result' => ['error' => 'No such bug']]); - exit; + echo json_encode(['result' => ['error' => 'No such bug']]); + exit; } if (!bugs_has_access($bug_id, $bug, $pwd, $user_flags)) { - echo json_encode(['result' => ['error' => 'No access to bug']]); - exit; + echo json_encode(['result' => ['error' => 'No access to bug']]); + exit; } if (!empty($_POST['ncomment']) && !empty($_POST['user'])) { - $user = htmlspecialchars(trim($_POST['user'])); - $ncomment = htmlspecialchars(trim($_POST['ncomment'])); - $from = "{$user}@php.net"; - - try { - /* svn log comment */ - bugs_add_comment($bug_id, $from, $user, $ncomment, 'svn'); - - /* Close the bug report as requested if it is not already closed */ - if (!empty($_POST['status']) - && $bug['status'] !== 'Closed' - && $_POST['status'] === 'Closed') { - /* Change the bug status to Closed */ - bugs_status_change($bug_id, 'Closed'); - - $in = $bug; - /* Just change the bug status */ - $in['status'] = $_POST['status']; - - $changed = bug_diff($bug, $in); - if (!empty($changed)) { - $log_comment = bug_diff_render_html($changed); - if (!empty($log_comment)) { - /* Add a log of status change */ - bugs_add_comment($bug_id, $from, '', $log_comment, 'log'); - } - } - - /* Send a mail notification when automatically closing a bug */ - mail_bug_updates($bug, $in, $from, $ncomment, 1, $bug_id); - } - - echo json_encode(['result' => ['status' => $bug]]); - exit; - } catch (Exception $e) { - echo json_encode(['result' => ['error' => $e->getMessage()]]); - exit; - } + $user = htmlspecialchars(trim($_POST['user'])); + $ncomment = htmlspecialchars(trim($_POST['ncomment'])); + $from = "{$user}@php.net"; + + try { + /* svn log comment */ + bugs_add_comment($bug_id, $from, $user, $ncomment, 'svn'); + + /* Close the bug report as requested if it is not already closed */ + if (!empty($_POST['status']) + && $bug['status'] !== 'Closed' + && $_POST['status'] === 'Closed') { + /* Change the bug status to Closed */ + bugs_status_change($bug_id, 'Closed'); + + $in = $bug; + /* Just change the bug status */ + $in['status'] = $_POST['status']; + + $changed = bug_diff($bug, $in); + if (!empty($changed)) { + $log_comment = bug_diff_render_html($changed); + if (!empty($log_comment)) { + /* Add a log of status change */ + bugs_add_comment($bug_id, $from, '', $log_comment, 'log'); + } + } + + /* Send a mail notification when automatically closing a bug */ + mail_bug_updates($bug, $in, $from, $ncomment, 1, $bug_id); + } + + echo json_encode(['result' => ['status' => $bug]]); + exit; + } catch (Exception $e) { + echo json_encode(['result' => ['error' => $e->getMessage()]]); + exit; + } } else if (!empty($_POST['getbug'])) { - echo json_encode(['result' => ['status' => $bug]]); - exit; + echo json_encode(['result' => ['status' => $bug]]); + exit; } echo json_encode(['result' => ['error' => 'Nothing to do']]); diff --git a/www/rss/bug.php b/www/rss/bug.php index 37a6b9f7..75cfbe7d 100644 --- a/www/rss/bug.php +++ b/www/rss/bug.php @@ -20,30 +20,30 @@ $bug = $bugRepository->findOneById($bug_id); if (!$bug) { - header('HTTP/1.0 404 Not Found'); - die('Nothing found'); + header('HTTP/1.0 404 Not Found'); + die('Nothing found'); } if ($bug['private'] == 'Y') { - header('HTTP/1.0 403 Forbidden'); - die('Access restricted'); + header('HTTP/1.0 403 Forbidden'); + die('Access restricted'); } $commentRepository = $container->get(CommentRepository::class); $comments = $commentRepository->findByBugId($bug_id); if ($format == 'xml') { - header('Content-type: text/xml; charset=utf-8'); - include './xml.php'; - exit; + header('Content-type: text/xml; charset=utf-8'); + include './xml.php'; + exit; } elseif ($format == "rss2") { - header('Content-type: application/rss+xml; charset=utf-8'); - $uri = "{$site_method}://{$site_url}{$basedir}/bug.php?id={$bug['id']}"; - include './rss.php'; - exit; + header('Content-type: application/rss+xml; charset=utf-8'); + $uri = "{$site_method}://{$site_url}{$basedir}/bug.php?id={$bug['id']}"; + include './rss.php'; + exit; } else { - header('Content-type: application/rdf+xml; charset=utf-8'); - $uri = "{$site_method}://{$site_url}{$basedir}/bug.php?id={$bug['id']}"; - include './rdf.php'; - exit; + header('Content-type: application/rdf+xml; charset=utf-8'); + $uri = "{$site_method}://{$site_url}{$basedir}/bug.php?id={$bug['id']}"; + include './rdf.php'; + exit; } diff --git a/www/rss/rdf.php b/www/rss/rdf.php index 3982af1b..04126bef 100644 --- a/www/rss/rdf.php +++ b/www/rss/rdf.php @@ -2,9 +2,9 @@ $desc = "{$bug['package_name']} {$bug['bug_type']}\nReported by "; if (preg_match('/@php.net$/i', $bug['email'])) { - $desc .= substr($bug['email'], 0, strpos($bug['email'], '@')) ."\n"; + $desc .= substr($bug['email'], 0, strpos($bug['email'], '@')) ."\n"; } else { - $desc .= substr($bug['email'], 0, strpos($bug['email'], '@')) . "@...\n"; + $desc .= substr($bug['email'], 0, strpos($bug['email'], '@')) . "@...\n"; } $desc .= date(DATE_ATOM, $bug['submitted']) . "\n"; $desc .= "PHP: {$bug['php_version']}, OS: {$bug['php_os']}\n\n"; @@ -13,116 +13,116 @@ $state = 'http://xmlns.com/baetle/#Open'; switch ($bug['status']) { - case 'Closed': - $state = 'http://xmlns.com/baetle/#Closed'; - break; - case 'Wont fix': - $state = 'http://xmlns.com/baetle/#WontFix'; - break; - case 'No Feedback': - $state = 'http://xmlns.com/baetle/#Incomplete'; - break; - case 'Not a bug': - $state = 'http://xmlns.com/baetle/#WorksForMe'; - break; - case 'Duplicate': - $state = 'http://xmlns.com/baetle/#Duplicate'; - break; - case 'Suspended': - $state = 'http://xmlns.com/baetle/#Later'; - break; - case 'Assigned': - $state = 'http://xmlns.com/baetle/#Started'; - break; - case 'Open': - $state = 'http://xmlns.com/baetle/#Open'; - break; - case 'Analyzed': - case 'Verified': - $state = 'http://xmlns.com/baetle/#Verified'; - break; - case 'Feedback': - $state = 'http://xmlns.com/baetle/#NotReproducable'; - break; + case 'Closed': + $state = 'http://xmlns.com/baetle/#Closed'; + break; + case 'Wont fix': + $state = 'http://xmlns.com/baetle/#WontFix'; + break; + case 'No Feedback': + $state = 'http://xmlns.com/baetle/#Incomplete'; + break; + case 'Not a bug': + $state = 'http://xmlns.com/baetle/#WorksForMe'; + break; + case 'Duplicate': + $state = 'http://xmlns.com/baetle/#Duplicate'; + break; + case 'Suspended': + $state = 'http://xmlns.com/baetle/#Later'; + break; + case 'Assigned': + $state = 'http://xmlns.com/baetle/#Started'; + break; + case 'Open': + $state = 'http://xmlns.com/baetle/#Open'; + break; + case 'Analyzed': + case 'Verified': + $state = 'http://xmlns.com/baetle/#Verified'; + break; + case 'Feedback': + $state = 'http://xmlns.com/baetle/#NotReproducable'; + break; } print ''; ?> - - <?php echo $bug['package_name']; ?> Bug #<?php echo intval($bug['id']); ?> - - + + <?php echo $bug['package_name']; ?> Bug #<?php echo intval($bug['id']); ?> + + - en-us - -webmaster@lists.php.net - -webmaster@lists.php.net + en-us + -webmaster@lists.php.net + -webmaster@lists.php.net - - hourly - 1 - 2000-01-01T12:00+00:00 + + hourly + 1 + 2000-01-01T12:00+00:00 - - - + + + - + - - - + + + - - - - - + + + + + - - <?php echo clean(substr($bug['email'], 0, strpos($bug['email'], '@'))), "@... [{$bug['ts1']}]"; ?> - - ]]> - ]]> - - + + <?php echo clean(substr($bug['email'], 0, strpos($bug['email'], '@'))), "@... [{$bug['ts1']}]"; ?> + + ]]> + ]]> + + - - + <item rdf:about="<?php echo $uri; ?>#<?php echo $comment['added']; ?>"> + <title> <?php - if ($comment['handle']) { - echo clean($comment['handle']) . " [$displayts]"; - } else { - echo clean(substr($comment['email'], 0, strpos($comment['email'], '@'))), "@... [$displayts]"; - } + if ($comment['handle']) { + echo clean($comment['handle']) . " [$displayts]"; + } else { + echo clean(substr($comment['email'], 0, strpos($comment['email'], '@'))), "@... [$displayts]"; + } ?> - + - # + # -
    ]]> -
    ]]> - - +
    ]]> +
    ]]> + + diff --git a/www/rss/rss.php b/www/rss/rss.php index b8427260..00749573 100644 --- a/www/rss/rss.php +++ b/www/rss/rss.php @@ -3,9 +3,9 @@ $desc = "{$bug['package_name']} {$bug['bug_type']}\nReported by "; if (preg_match('/@php.net$/i', $bug['email'])) { - $desc .= substr($bug['email'], 0, strpos($bug['email'], '@')) ."\n"; + $desc .= substr($bug['email'], 0, strpos($bug['email'], '@')) ."\n"; } else { - $desc .= substr($bug['email'], 0, strpos($bug['email'], '@')) . "@...\n"; + $desc .= substr($bug['email'], 0, strpos($bug['email'], '@')) . "@...\n"; } $desc .= date(DATE_RSS, $bug['submitted']) . "\n"; $desc .= "PHP: {$bug['php_version']}, OS: {$bug['php_os']}\n\n"; @@ -14,30 +14,30 @@ ?> - - <?php echo "{$bug['package_name']} Bug #{$bug['id']}"; ?> - - - - - " rel="self" type="application/rss+xml" /> - - <?php echo ($bug['assign']) ? clean($bug['assign']) : clean(substr($bug['email'], 0, strpos($bug['email'], '@'))), "@... [{$bug['ts1']}]"; ?> - ]]> - - - + + <?php echo "{$bug['package_name']} Bug #{$bug['id']}"; ?> + + + + + " rel="self" type="application/rss+xml" /> + + <?php echo ($bug['assign']) ? clean($bug['assign']) : clean(substr($bug['email'], 0, strpos($bug['email'], '@'))), "@... [{$bug['ts1']}]"; ?> + ]]> + + + - - <?php echo clean($comment['email'] . " [$displayts]"); ?> - ', clean($comment['comment']), ''; ?>]]> - - - + + <?php echo clean($comment['email'] . " [$displayts]"); ?> + ', clean($comment['comment']), ''; ?>]]> + + + - + diff --git a/www/rss/search.php b/www/rss/search.php index 07394049..ad6c7a00 100644 --- a/www/rss/search.php +++ b/www/rss/search.php @@ -17,11 +17,11 @@ // Maximum number of bugs to return if ($format === 'rss2') { - // RSS channel shows way more data (e.g. no bug description) thus - // we can fetch more rows - define ('MAX_BUGS_RETURN', 500); + // RSS channel shows way more data (e.g. no bug description) thus + // we can fetch more rows + define ('MAX_BUGS_RETURN', 500); } else { - define ('MAX_BUGS_RETURN', 150); + define ('MAX_BUGS_RETURN', 150); } // Obtain common includes @@ -29,16 +29,16 @@ require "{$ROOT_DIR}/include/query.php"; if ($format === 'rss2') { - require "{$ROOT_DIR}/templates/search-rss2.php"; + require "{$ROOT_DIR}/templates/search-rss2.php"; } else { - require "{$ROOT_DIR}/templates/search-rdf.php"; + require "{$ROOT_DIR}/templates/search-rdf.php"; } if (count($warnings) > 0) { - echo "\n"; + echo "\n"; } diff --git a/www/rss/xml.php b/www/rss/xml.php index eebdae47..188ebe04 100644 --- a/www/rss/xml.php +++ b/www/rss/xml.php @@ -1,16 +1,16 @@ \n"; foreach ($bug as $key => $value) { - if (is_int($key)) continue; - echo " <$key>", clean($value), "\n"; + if (is_int($key)) continue; + echo " <$key>", clean($value), "\n"; } foreach ($comments as $comment) { - if (empty($comment['registered'])) continue; - echo " \n"; - foreach ($comment as $key => $value) { - if (is_int($key)) continue; - echo " <$key>", clean($value), "\n"; - } - echo " \n"; + if (empty($comment['registered'])) continue; + echo " \n"; + foreach ($comment as $key => $value) { + if (is_int($key)) continue; + echo " <$key>", clean($value), "\n"; + } + echo " \n"; } echo "\n"; diff --git a/www/search.php b/www/search.php index 1edd193c..27542fc0 100644 --- a/www/search.php +++ b/www/search.php @@ -10,7 +10,7 @@ // Redirect early if a bug id is passed as search string if (isset($_GET['search_for']) && preg_match('/^\d+$/', trim($_GET['search_for']), $search_for_id_array)) { - redirect("bug.php?id=${search_for_id_array[0]}"); + redirect("bug.php?id=${search_for_id_array[0]}"); } // For bug count only, used in places like doc.php.net @@ -23,11 +23,11 @@ $newrequest = http_build_query(array_merge($_GET, $_POST)); if (!$count_only) { - response_header( - 'Bugs :: Search', " - - - "); + response_header( + 'Bugs :: Search', " + + + "); } // Include common query handler (used also by rss/search.php) @@ -35,86 +35,86 @@ if (isset($_GET['cmd']) && $_GET['cmd'] == 'display') { - // FIXME: this if doesn't make sense, check is already performed in - // query.php - whole condition can be removed, reducing level of - // nesting by one. - if (!isset($result)) { - $errors[] = 'Invalid query'; - } else { - // For count only, simply print the count and exit - if ($count_only) { - echo (int) $total_rows; - exit; - } - - // Selected packages to search in - $package_name_string = ''; - if (count($package_name) > 0) { - foreach ($package_name as $type_str) { - $package_name_string.= '&package_name[]=' . urlencode($type_str); - } - } - - // Selected packages NOT to search in - $package_nname_string = ''; - if (count($package_nname) > 0) { - foreach ($package_nname as $type_str) { - $package_nname_string.= '&package_nname[]=' . urlencode($type_str); - } - } - - $link_params = [ - 'search_for' => urlencode($search_for), - 'project' => urlencode($project), - 'php_os' => urlencode($php_os), - 'php_os_not' => $php_os_not, - 'author_email' => urlencode($author_email), - 'bug_type' => urlencode($bug_type), - 'boolean' => $boolean_search, - 'bug_age' => $bug_age, - 'bug_updated' => $bug_updated, - 'order_by' => $order_by, - 'direction' => $direction, - 'limit' => $limit, - 'phpver' => urlencode($phpver), - 'cve_id' => urlencode($cve_id), - 'cve_id_not' => $cve_id_not, - 'patch' => urlencode($patch), - 'pull' => urlencode($pull), - 'assign' => urlencode($assign), - 'commented_by' => urlencode($commented_by), - ]; - - if ($is_security_developer) { - $link_params['private'] = $private; - } - - // Remove empty URL parameters - foreach ($link_params as $index => $param) { - if (empty($param)) - unset($link_params[$index]); - } - - // Create link params string - $link_params_string = ''; - foreach ($link_params as $index => $param) { - $link_params_string .= "&$index=$param"; - } - - $link = "search.php?cmd=display{$package_name_string}{$package_nname_string}{$link_params_string}"; - $clean_link = "search.php?cmd=display{$link_params_string}"; - - if (isset($_GET['showmenu'])) { - $link .= '&showmenu=1'; - } - - if (!$rows) { - $errors[] = 'No bugs were found.'; - display_bug_error($errors, 'warnings', ''); - } else { - display_bug_error($warnings, 'warnings', 'WARNING:'); - $link .= '&status=' . urlencode($status); - $package_count = count($package_name); + // FIXME: this if doesn't make sense, check is already performed in + // query.php - whole condition can be removed, reducing level of + // nesting by one. + if (!isset($result)) { + $errors[] = 'Invalid query'; + } else { + // For count only, simply print the count and exit + if ($count_only) { + echo (int) $total_rows; + exit; + } + + // Selected packages to search in + $package_name_string = ''; + if (count($package_name) > 0) { + foreach ($package_name as $type_str) { + $package_name_string.= '&package_name[]=' . urlencode($type_str); + } + } + + // Selected packages NOT to search in + $package_nname_string = ''; + if (count($package_nname) > 0) { + foreach ($package_nname as $type_str) { + $package_nname_string.= '&package_nname[]=' . urlencode($type_str); + } + } + + $link_params = [ + 'search_for' => urlencode($search_for), + 'project' => urlencode($project), + 'php_os' => urlencode($php_os), + 'php_os_not' => $php_os_not, + 'author_email' => urlencode($author_email), + 'bug_type' => urlencode($bug_type), + 'boolean' => $boolean_search, + 'bug_age' => $bug_age, + 'bug_updated' => $bug_updated, + 'order_by' => $order_by, + 'direction' => $direction, + 'limit' => $limit, + 'phpver' => urlencode($phpver), + 'cve_id' => urlencode($cve_id), + 'cve_id_not' => $cve_id_not, + 'patch' => urlencode($patch), + 'pull' => urlencode($pull), + 'assign' => urlencode($assign), + 'commented_by' => urlencode($commented_by), + ]; + + if ($is_security_developer) { + $link_params['private'] = $private; + } + + // Remove empty URL parameters + foreach ($link_params as $index => $param) { + if (empty($param)) + unset($link_params[$index]); + } + + // Create link params string + $link_params_string = ''; + foreach ($link_params as $index => $param) { + $link_params_string .= "&$index=$param"; + } + + $link = "search.php?cmd=display{$package_name_string}{$package_nname_string}{$link_params_string}"; + $clean_link = "search.php?cmd=display{$link_params_string}"; + + if (isset($_GET['showmenu'])) { + $link .= '&showmenu=1'; + } + + if (!$rows) { + $errors[] = 'No bugs were found.'; + display_bug_error($errors, 'warnings', ''); + } else { + display_bug_error($warnings, 'warnings', 'WARNING:'); + $link .= '&status=' . urlencode($status); + $package_count = count($package_name); ?> @@ -125,9 +125,9 @@ @@ -149,62 +149,62 @@ ' , "\n"; + echo ' ' , "\n"; - // Bug ID - echo ' ', "\n"; + // Bug ID + echo ' ', "\n"; - // Date - echo ' \n"; + // Date + echo ' \n"; - // Last Modified - $ts2 = strtotime($row['ts2']); - echo ' \n"; + // Last Modified + $ts2 = strtotime($row['ts2']); + echo ' \n"; - // Package - if ($package_count !== 1) { - $pck = htmlspecialchars($row['package_name']); - $pck_url = urlencode($pck); - echo "\n"; - } + // Package + if ($package_count !== 1) { + $pck = htmlspecialchars($row['package_name']); + $pck_url = urlencode($pck); + echo "\n"; + } - /// Bug type - $type_idx = !empty($row['bug_type']) ? $row['bug_type'] : 'Bug'; - echo ' ', "\n"; + /// Bug type + $type_idx = !empty($row['bug_type']) ? $row['bug_type'] : 'Bug'; + echo ' ', "\n"; - // Status - echo ' ', "\n"; + // Status + echo ' ', "\n"; - /// PHP version - echo ' '; + /// PHP version + echo ' '; - // OS - echo ' ', "\n"; + // OS + echo ' ', "\n"; - // Short description - echo ' ', "\n"; + // Short description + echo ' ', "\n"; - // Assigned to - echo ' '; - echo " \n"; - } + // Assigned to + echo ' '; + echo " \n"; + } - show_prev_next($begin, $rows, $total_rows, $link, $limit); + show_prev_next($begin, $rows, $total_rows, $link, $limit); - echo "
    ', $row['id'], ''; - echo '
    (edit)
    ', $row['id'], ''; + echo '
    (edit)
    ', format_date(strtotime($row['ts1'])), "', format_date(strtotime($row['ts1'])), "' , ($ts2 ? format_date($ts2) : 'Not modified') , "' , ($ts2 ? format_date($ts2) : 'Not modified') , "{$pck}{$pck}', htmlspecialchars($bug_types[$type_idx]), '', htmlspecialchars($bug_types[$type_idx]), '', htmlspecialchars($row['status']); - if ($row['status'] == 'Feedback' && $row['unchanged'] > 0) { - printf ("
    %d day%s", $row['unchanged'], $row['unchanged'] > 1 ? 's' : ''); - } - echo '
    ', htmlspecialchars($row['status']); + if ($row['status'] == 'Feedback' && $row['unchanged'] > 0) { + printf ("
    %d day%s", $row['unchanged'], $row['unchanged'] > 1 ? 's' : ''); + } + echo '
    ', htmlspecialchars($row['php_version']), '', htmlspecialchars($row['php_version']), '', $row['php_os'] ? htmlspecialchars($row['php_os']) : ' ', '', $row['php_os'] ? htmlspecialchars($row['php_os']) : ' ', '', $row['sdesc'] ? htmlspecialchars($row['sdesc']) : ' ', '', $row['sdesc'] ? htmlspecialchars($row['sdesc']) : ' ', '', ($row['assign'] ? ("' . htmlspecialchars($row['assign']) . '') : ' '), '
    ', ($row['assign'] ? ("' . htmlspecialchars($row['assign']) . '') : ' '), '
    \n\n"; - } + echo "\n\n"; + } - response_footer(); - exit; - } + response_footer(); + exit; + } } display_bug_error($errors); @@ -228,9 +228,9 @@
    - >Ascending -   - >Descending + >Ascending +   + >Descending

    @@ -307,10 +307,10 @@ Return bugs assigned to handle)) { - $u = htmlspecialchars($auth_user->handle); - echo ""; - } + if (!empty($auth_user->handle)) { + $u = htmlspecialchars($auth_user->handle); + echo ""; + } ?> @@ -320,10 +320,10 @@ Return bugs with author email handle)) { - $u = htmlspecialchars($auth_user->handle); - echo ""; - } + if (!empty($auth_user->handle)) { + $u = htmlspecialchars($auth_user->handle); + echo ""; + } ?> @@ -347,12 +347,12 @@ > - Commented by - Return bugs that have been commented by - + Commented by + Return bugs that have been commented by + Private @@ -368,46 +368,46 @@ function show_prev_next($begin, $rows, $total_rows, $link, $limit) { - echo "\n"; - echo " \n"; - echo ' ' . "\n"; - - if ($limit=='All') { - echo "$total_rows Bugs\n"; - return; - } - - echo ' ' . "\n"; - echo " \n"; - echo ' \n"; - - echo ' \n"; - - echo ' \n \n
    '; - if ($begin > 0) { - echo '« Show Previous ' . $limit . ' Entries'; - } else { - echo ' '; - } - echo "Showing ' . ($begin+1); - echo '-' . ($begin+$rows) . ' of ' . $total_rows . "'; - if ($begin+$rows < $total_rows) { - echo 'Show Next ' . $limit . ' Entries »'; - } else { - echo ' '; - } - echo "
    \n \n \n"; - echo "\n"; + echo "\n"; + echo " \n"; + echo ' ' . "\n"; + + if ($limit=='All') { + echo "$total_rows Bugs\n"; + return; + } + + echo ' ' . "\n"; + echo " \n"; + echo ' \n"; + + echo ' \n"; + + echo ' \n \n
    '; + if ($begin > 0) { + echo '« Show Previous ' . $limit . ' Entries'; + } else { + echo ' '; + } + echo "Showing ' . ($begin+1); + echo '-' . ($begin+$rows) . ' of ' . $total_rows . "'; + if ($begin+$rows < $total_rows) { + echo 'Show Next ' . $limit . ' Entries »'; + } else { + echo ' '; + } + echo "
    \n \n \n"; + echo "\n"; } function show_order_options($current) { - global $order_options; + global $order_options; - foreach ($order_options as $k => $v) { - echo '\n"; - } + foreach ($order_options as $k => $v) { + echo '\n"; + } } diff --git a/www/stats.php b/www/stats.php index 243a1187..aaac6886 100644 --- a/www/stats.php +++ b/www/stats.php @@ -13,18 +13,18 @@ response_header('Bugs Stats'); $titles = [ - 'Closed' => 'Closed', - 'Open' => 'Open', - 'Critical' => 'Crit', - 'Verified' => 'Verified', - 'Analyzed' => 'Analyzed', - 'Assigned' => 'Assigned', - 'Feedback' => 'Fdbk', - 'No Feedback' => 'No Fdbk', - 'Suspended' => 'Susp', - 'Not a bug' => 'Not a bug', - 'Duplicate' => 'Dupe', - 'Wont fix' => 'Wont Fix', + 'Closed' => 'Closed', + 'Open' => 'Open', + 'Critical' => 'Crit', + 'Verified' => 'Verified', + 'Analyzed' => 'Analyzed', + 'Assigned' => 'Assigned', + 'Feedback' => 'Fdbk', + 'No Feedback' => 'No Fdbk', + 'Suspended' => 'Susp', + 'Not a bug' => 'Not a bug', + 'Duplicate' => 'Dupe', + 'Wont fix' => 'Wont Fix', ]; $rev = isset($_GET['rev']) ? $_GET['rev'] : 1; @@ -36,54 +36,54 @@ $pkg_total = []; $pkg_names = []; $all = []; -$pseudo = true; +$pseudo = true; if (!array_key_exists($sort_by, $titles)) { - $sort_by = 'Open'; + $sort_by = 'Open'; } $bug_type = $_GET['bug_type'] ?? 'All'; $bugRepository = $container->get(BugRepository::class); foreach ($bugRepository->findAllByBugType($bug_type) as $row) { - $pkg_tmp[$row['status']][$row['package_name']] = $row['quant']; - @$pkg_total[$row['package_name']] += $row['quant']; - @$all[$row['status']] += $row['quant']; - @$total += $row['quant']; - $pkg_names[$row['package_name']] = 0; + $pkg_tmp[$row['status']][$row['package_name']] = $row['quant']; + @$pkg_total[$row['package_name']] += $row['quant']; + @$all[$row['status']] += $row['quant']; + @$total += $row['quant']; + $pkg_names[$row['package_name']] = 0; } if (count($pkg_tmp)) { - foreach ($titles as $key => $val) { - if (isset($pkg_tmp[$key]) && is_array($pkg_tmp[$key])) { - $pkg[$key] = array_merge($pkg_names, $pkg_tmp[$key]); - } else { - $pkg[$key] = $pkg_names; - } - } + foreach ($titles as $key => $val) { + if (isset($pkg_tmp[$key]) && is_array($pkg_tmp[$key])) { + $pkg[$key] = array_merge($pkg_names, $pkg_tmp[$key]); + } else { + $pkg[$key] = $pkg_names; + } + } } if ($total > 0) { - if ($rev == 1) { - arsort($pkg[$sort_by]); - } else { - asort($pkg[$sort_by]); - } + if ($rev == 1) { + arsort($pkg[$sort_by]); + } else { + asort($pkg[$sort_by]); + } } ?>
    - - - - -
    - Bug Type: - - -
    + + + + +
    + Bug Type: + + +
    @@ -91,65 +91,65 @@
    No bugs found
    ' . "\n"; - response_footer(); - exit; + echo 'No bugs found' . "\n"; + response_footer(); + exit; } echo display_stat_header($total, true); echo <<< OUTPUT - - All - {$total} + + All + {$total} OUTPUT; $i = 1; foreach ($titles as $key => $val) { - echo ' '; - echo bugstats($key, 'all') , "\n"; + echo ' '; + echo bugstats($key, 'all') , "\n"; } -echo " \n"; +echo " \n"; $stat_row = 1; foreach ($pkg[$sort_by] as $name => $value) { - if ($name != 'all') { - // Output a new header row every 40 lines - if (($stat_row++ % 40) == 0) { - echo display_stat_header($total, false); - } - echo <<< OUTPUT - - {$name} - {$pkg_total[$name]} + if ($name != 'all') { + // Output a new header row every 40 lines + if (($stat_row++ % 40) == 0) { + echo display_stat_header($total, false); + } + echo <<< OUTPUT + + {$name} + {$pkg_total[$name]} OUTPUT; - $i = 1; - foreach ($titles as $key => $val) { - echo ' '; - echo bugstats($key, $name), "\n"; - } - echo " \n"; - } + $i = 1; + foreach ($titles as $key => $val) { + echo ' '; + echo bugstats($key, $name), "\n"; + } + echo " \n"; + } } echo "\n
    \n

    PHP Versions for recent bug reports:

    "; $last_date = null; foreach ($bugRepository->findPhpVersions($bug_type) as $row) { - if ($row['d'] != $last_date) { - if ($last_date !== null) { - echo "\n\n"; - } - echo "\n". - "\n"; - $last_date = $row['d']; - } - $version = htmlentities($row["formatted_version"], ENT_QUOTES, 'UTF-8'); - echo "\n"; + if ($row['d'] != $last_date) { + if ($last_date !== null) { + echo "
    {$row["d"]}
    {$version}{$row["quant"]}
    \n\n"; + } + echo "\n". + "\n"; + $last_date = $row['d']; + } + $version = htmlentities($row["formatted_version"], ENT_QUOTES, 'UTF-8'); + echo "\n"; } if ($last_date) { - echo "
    {$row["d"]}
    {$version}{$row["quant"]}
    \n"; + echo "\n"; } echo "
    \n"; @@ -159,64 +159,64 @@ function bugstats($status, $name) { - global $pkg, $all, $bug_type; - - if ($name == 'all') { - if (isset($all[$status])) { - return '' . - $all[$status] . "\n"; - } - } else { - if (empty($pkg[$status][$name])) { - return ' '; - } else { - return '' . - $pkg[$status][$name] . "\n"; - } - } + global $pkg, $all, $bug_type; + + if ($name == 'all') { + if (isset($all[$status])) { + return '' . + $all[$status] . "\n"; + } + } else { + if (empty($pkg[$status][$name])) { + return ' '; + } else { + return '' . + $pkg[$status][$name] . "\n"; + } + } } function sort_url(http://github.com/$name) { - global $sort_by, $rev, $titles; - - if ($name == $sort_by) { - $reve = (int) !$rev; - } else { - $reve = 1; - } - if ($sort_by != $name) { - $attr = 'class="bug_stats"'; - } else { - $attr = 'class="bug_stats_choosen"'; - } - return '' . - $titles[$name] . ''; + global $sort_by, $rev, $titles; + + if ($name == $sort_by) { + $reve = (int) !$rev; + } else { + $reve = 1; + } + if ($sort_by != $name) { + $attr = 'class="bug_stats"'; + } else { + $attr = 'class="bug_stats_choosen"'; + } + return '' . + $titles[$name] . ''; } function display_stat_header($total, $grandtotal = true) { - global $titles; - - $stat_head = " \n"; - if ($grandtotal) { - $stat_head .= " Name\n"; - } else { - $stat_head .= "  \n"; - } - $stat_head .= "  \n"; - - foreach ($titles as $key => $val) { - $stat_head .= ' ' . sort_url(http://github.com/$key) . "\n"; - } - - $stat_head .= '' . "\n"; - return $stat_head; + global $titles; + + $stat_head = " \n"; + if ($grandtotal) { + $stat_head .= " Name\n"; + } else { + $stat_head .= "  \n"; + } + $stat_head .= "  \n"; + + foreach ($titles as $key => $val) { + $stat_head .= ' ' . sort_url(http://github.com/$key) . "\n"; + } + + $stat_head .= '' . "\n"; + return $stat_head; } diff --git a/www/vote.php b/www/vote.php index ab3b675e..e32f21d0 100644 --- a/www/vote.php +++ b/www/vote.php @@ -8,13 +8,13 @@ $id = isset($_POST['id']) ? (int) $_POST['id'] : 0; if (empty($id)) { - die('invalid bug id'); + die('invalid bug id'); } if (!isset($_POST['score'])) die("missing parameter score"); $score = (int) $_POST['score']; if ($score < -2 || $score > 2) { - die("invalid score: $score"); + die("invalid score: $score"); } if (!isset($_POST['reproduced'])) die("missing parameter reproduced"); @@ -24,40 +24,40 @@ $sameos = isset($_POST['sameos']) ? (int) $_POST['sameos'] : 0; if (!$container->get(BugRepository::class)->exists($id)) { - session_start(); + session_start(); - // Authenticate - bugs_authenticate($user, $pw, $logged_in, $user_flags); + // Authenticate + bugs_authenticate($user, $pw, $logged_in, $user_flags); - response_header('No such bug.'); - display_bug_error("No such bug #{$id}"); - response_footer(); - exit; + response_header('No such bug.'); + display_bug_error("No such bug #{$id}"); + response_footer(); + exit; } // Figure out which IP the user is coming from avoiding RFC 1918 space function get_real_ip () { - $ip = false; + $ip = false; - // User is behind a proxy and check that we discard RFC1918 IP - // addresses if they are behind a proxy then only figure out which - // IP belongs to the user. Might not need any more hacking if - // there is a squid reverse proxy infront of apache. - if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { - $ips = explode (", ", $_SERVER['HTTP_X_FORWARDED_FOR']); - if ($ip) { array_unshift($ips, $ip); $ip = false; } - for ($i = 0; $i < count($ips); $i++) { - // Skip RFC 1918 IP's 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16 - // -- jim kill me later with my regexp pattern below. - if (!eregi ("^(10|172\.16|192\.168)\.", $ips[$i]) && - preg_match("/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/", $ips[$i])) { - $ip = $ips[$i]; - break; - } - } - } - return ($ip ? $ip : $_SERVER['REMOTE_ADDR']); + // User is behind a proxy and check that we discard RFC1918 IP + // addresses if they are behind a proxy then only figure out which + // IP belongs to the user. Might not need any more hacking if + // there is a squid reverse proxy infront of apache. + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $ips = explode (", ", $_SERVER['HTTP_X_FORWARDED_FOR']); + if ($ip) { array_unshift($ips, $ip); $ip = false; } + for ($i = 0; $i < count($ips); $i++) { + // Skip RFC 1918 IP's 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16 + // -- jim kill me later with my regexp pattern below. + if (!eregi ("^(10|172\.16|192\.168)\.", $ips[$i]) && + preg_match("/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/", $ips[$i])) { + $ip = $ips[$i]; + break; + } + } + } + return ($ip ? $ip : $_SERVER['REMOTE_ADDR']); } $ip = ip2long(get_real_ip()); @@ -65,36 +65,36 @@ function get_real_ip () // Check whether the user has already voted on this bug. if (empty($container->get(VoteRepository::class)->findOneByIdAndIp($id, $ip))) { - // If the user vote isn't found, create one. - $dbh->prepare(" - INSERT INTO bugdb_votes (bug, ip, score, reproduced, tried, sameos, samever) - VALUES ( - {$id}, {$ip}, {$score}, " . - ($reproduced == 1 ? "1," : "0,") . - ($reproduced != 2 ? "1," : "0,") . - ($reproduced ? "$sameos," : "NULL,") . - ($reproduced ? "$samever" : "NULL") . - ')' - )->execute(); + // If the user vote isn't found, create one. + $dbh->prepare(" + INSERT INTO bugdb_votes (bug, ip, score, reproduced, tried, sameos, samever) + VALUES ( + {$id}, {$ip}, {$score}, " . + ($reproduced == 1 ? "1," : "0,") . + ($reproduced != 2 ? "1," : "0,") . + ($reproduced ? "$sameos," : "NULL,") . + ($reproduced ? "$samever" : "NULL") . + ')' + )->execute(); - // redirect to the bug page (which will display the success message) - redirect("bug.php?id=$id&thanks=6"); + // redirect to the bug page (which will display the success message) + redirect("bug.php?id=$id&thanks=6"); } else { - // As the user has already voted, just update their existing vote. - $dbh->prepare("UPDATE bugdb_votes - SET score = ?, reproduced = ? , tried = ?, sameos = ?, samever = ? - WHERE bug = ? AND ip = ?") - ->execute([ - $score, - ($reproduced == 1 ? "1" : "0"), - ($reproduced != 2 ? "1" : "0"), - ($reproduced ? "$sameos" : null), - ($reproduced ? "$samever" : null), - $id, - $ip - ]); + // As the user has already voted, just update their existing vote. + $dbh->prepare("UPDATE bugdb_votes + SET score = ?, reproduced = ? , tried = ?, sameos = ?, samever = ? + WHERE bug = ? AND ip = ?") + ->execute([ + $score, + ($reproduced == 1 ? "1" : "0"), + ($reproduced != 2 ? "1" : "0"), + ($reproduced ? "$sameos" : null), + ($reproduced ? "$samever" : null), + $id, + $ip + ]); - // Let the user know they have already voted and the existing vote will be - // updated. - redirect("bug.php?id=$id&thanks=10"); + // Let the user know they have already voted and the existing vote will be + // updated. + redirect("bug.php?id=$id&thanks=10"); } From 5cdac95adc49562291a873c8704de3ceb0d9d398 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 6 May 2019 10:53:06 +0200 Subject: [PATCH 194/277] Add automatic fixtures generator importer When developing site locally, developer needs to have data sets to run manual tests, see how the app is built, and similar. The new additional command line command using Symfony/Console component creates a main scripts/console entry point for running Commands. Initial set of demo fixtures data is generated using the faker library and includes most data needed to use and test application manually in the local environment. --- .gitignore | 2 +- README.md | 12 +- composer.json | 4 +- composer.lock | 254 ++++++- config/container.php | 21 + config/parameters.php | 20 + scripts/console | 15 + sql/fixtures.sql | 4 - src/Command/InsertFixturesCommand.php | 100 +++ src/Fixtures/AppFixtures.php | 645 ++++++++++++++++++ .../Command/InsertFixturesCommandTest.php | 65 ++ 11 files changed, 1132 insertions(+), 10 deletions(-) create mode 100755 scripts/console delete mode 100644 sql/fixtures.sql create mode 100644 src/Command/InsertFixturesCommand.php create mode 100644 src/Fixtures/AppFixtures.php create mode 100644 tests/Unit/Command/InsertFixturesCommandTest.php diff --git a/.gitignore b/.gitignore index 8d0441ea..fe3c3ebd 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ # Local specific PHPUnit configuration /phpunit.xml -.phpunit.result.cache +/.phpunit.result.cache # Transient and temporary generated files /var/ diff --git a/README.md b/README.md index d3421a25..f767e564 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,13 @@ Modify `local_config.php` according to your local development environment. * Database: -Create a new MySQL/MariaDB database using `sql/database.sql`, create database -schema `sql/schema.sql` and insert fixtures using `sql/fixtures.sql`. +Create a new MySQL, MariaDB, Percona Server, or equivalent database using +`sql/database.sql`, create database schema `sql/schema.sql` and insert demo data +fixtures by running: + +```bash +php scripts/console app:fixtures:insert +``` ## Tests @@ -42,9 +47,10 @@ Source code of this application is structured in the following directories: ├─ prepend.php # Autoloader, DB connection, container, app initialization └─ ... └─ scripts/ # Command line development tools and scripts + ├─ console # Application's main script for running CLI commands ├─ cron/ # Various systems scripts to run periodically on the server └─ ... - ├─ sql/ # Database schema and fixtures + ├─ sql/ # Database schema └─ src/ # Application source code classes ├─ Horde/ # https://www.horde.org/libraries/Horde_Text_Diff └─ ... diff --git a/composer.json b/composer.json index abec91de..4d022655 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,9 @@ }, "require-dev": { "ext-pdo_sqlite": "*", - "phpunit/phpunit": "^8.1.5" + "fzaninotto/faker": "^1.8", + "phpunit/phpunit": "^8.1.5", + "symfony/console": "^4.2" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 81116187..898739c0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "994d11b989a10210dc468c7046539905", + "content-hash": "e90209efe832cd89e50a5f742fd551a8", "packages": [], "packages-dev": [ { @@ -63,6 +63,56 @@ ], "time": "2019-03-17T17:37:11+00:00" }, + { + "name": "fzaninotto/faker", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/fzaninotto/Faker.git", + "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de", + "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "ext-intl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7", + "squizlabs/php_codesniffer": "^1.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "time": "2018-07-12T10:23:15+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.9.1", @@ -1331,6 +1381,149 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2016-10-03T07:35:21+00:00" }, + { + "name": "symfony/console", + "version": "v4.2.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "e2840bb38bddad7a0feaf85931e38fdcffdb2f81" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/e2840bb38bddad7a0feaf85931e38fdcffdb2f81", + "reference": "e2840bb38bddad7a0feaf85931e38fdcffdb2f81", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/contracts": "^1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com/", + "time": "2019-04-08T14:23:48+00:00" + }, + { + "name": "symfony/contracts", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/contracts.git", + "reference": "d3636025e8253c6144358ec0a62773cae588395b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/contracts/zipball/d3636025e8253c6144358ec0a62773cae588395b", + "reference": "d3636025e8253c6144358ec0a62773cae588395b", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "require-dev": { + "psr/cache": "^1.0", + "psr/container": "^1.0", + "symfony/polyfill-intl-idn": "^1.10" + }, + "suggest": { + "psr/cache": "When using the Cache contracts", + "psr/container": "When using the Service contracts", + "symfony/cache-contracts-implementation": "", + "symfony/event-dispatcher-implementation": "", + "symfony/http-client-contracts-implementation": "", + "symfony/service-contracts-implementation": "", + "symfony/translation-contracts-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\": "" + }, + "exclude-from-classmap": [ + "**/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A set of abstractions extracted out of the Symfony components", + "homepage": "https://symfony.com/", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "time": "2019-04-27T14:29:50+00:00" + }, { "name": "symfony/polyfill-ctype", "version": "v1.11.0", @@ -1389,6 +1582,65 @@ ], "time": "2019-02-06T07:57:58+00:00" }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", + "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com/", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2019-02-06T07:57:58+00:00" + }, { "name": "theseer/tokenizer", "version": "1.1.2", diff --git a/config/container.php b/config/container.php index 5cf29795..59e30457 100644 --- a/config/container.php +++ b/config/container.php @@ -81,4 +81,25 @@ return new App\Utils\Uploader(); }); +$container->set(App\Fixtures\AppFixtures::class, function ($c) { + return new App\Fixtures\AppFixtures($c->get(\PDO::class), Faker\Factory::create(), $c->get('bug_statuses')); +}); + +$container->set(App\Command\InsertFixturesCommand::class, function ($c) { + $insertFixturesCommand = new App\Command\InsertFixturesCommand( + $c->get(\PDO::class), + $c->get(App\Fixtures\AppFixtures::class), + $c->get('env') + ); + + return $insertFixturesCommand; +}); + +$container->set(Symfony\Component\Console\Application::class, function ($c) { + $application = new Symfony\Component\Console\Application(); + $application->add($c->get(App\Command\InsertFixturesCommand::class)); + + return $application; +}); + return $container; diff --git a/config/parameters.php b/config/parameters.php index 9ff0d78a..0cf1b723 100644 --- a/config/parameters.php +++ b/config/parameters.php @@ -70,4 +70,24 @@ * Templates directory. */ 'templates_dir' => __DIR__.'/../templates', + + /** + * Bug statuses. + */ + 'bug_statuses' => [ + 'Open' => 'Opn', + 'Not a bug' => 'Nab', + 'Feedback' => 'Fbk', + 'No Feedback' => 'NoF', + 'Wont fix' => 'Wfx', + 'Duplicate' => 'Dup', + 'Critical' => 'Ctl', + 'Assigned' => 'Asn', + 'Analyzed' => 'Ana', + 'Verified' => 'Ver', + 'Suspended' => 'Sus', + 'Closed' => 'Csd', + 'Spam' => 'Spm', + 'Re-Opened' => 'ReO', + ], ]; diff --git a/scripts/console b/scripts/console new file mode 100755 index 00000000..6c2f44f0 --- /dev/null +++ b/scripts/console @@ -0,0 +1,15 @@ +#!/usr/bin/env php +] [arguments] + */ + +require_once __DIR__.'/../include/prepend.php'; + +use Symfony\Component\Console\Application; + +$container->get(Application::class)->run(); diff --git a/sql/fixtures.sql b/sql/fixtures.sql deleted file mode 100644 index c4f3bf79..00000000 --- a/sql/fixtures.sql +++ /dev/null @@ -1,4 +0,0 @@ --- Default pseudo packages (common for all projects) - -INSERT INTO bugdb_pseudo_packages SET id = '1', parent = '0', name = 'Web Site', long_name = 'Web Site', project = ''; -INSERT INTO bugdb_pseudo_packages SET id = '2', parent = '1', name = 'Bug System', long_name = 'Bug System', project = ''; diff --git a/src/Command/InsertFixturesCommand.php b/src/Command/InsertFixturesCommand.php new file mode 100644 index 00000000..b833f2ce --- /dev/null +++ b/src/Command/InsertFixturesCommand.php @@ -0,0 +1,100 @@ +dbh = $dbh; + $this->fixtures = $fixtures; + $this->environment = $environment; + + parent::__construct(); + } + + /** + * Configure command. + */ + protected function configure() + { + $this->setName('app:fixtures:insert') + ->setDescription('Insert database fixtures.') + ->setHelp('This command inserts demo data fixtures in the database.') + ; + } + + /** + * Run the command, for example, bin/console app:generate-fixtures. It can + * be executed only in development environment as a safety measure to not + * delete something important. + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if ('dev' !== $this->environment) { + $output->writeln('This command can be executed only in development.'); + + return; + } + + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion( + 'This will erase entire database. Are you sure you want to continue? [y/N]', + false + ); + + if (!$helper->ask($input, $output, $question)) { + $output->writeln('Exiting...'); + + return; + } + + // Delete all existing categories and add new ones. + $output->writeln('Deleting existing categories'); + $this->dbh->query('DELETE FROM bugdb_pseudo_packages'); + $output->writeln('Adding new categories'); + $this->fixtures->insertCategories(); + + // Delete all current bug reports and add new ones. + $output->writeln('Deleting existing bug reports'); + $this->dbh->query('DELETE FROM bugdb'); + $output->writeln('Adding new bug reports'); + $this->fixtures->insertBugs(); + + // Delete all current reasons and add new ones. + $output->writeln('Deleting existing reasons'); + $this->dbh->query('DELETE FROM bugdb_resolves'); + $output->writeln('Adding new reasons'); + $this->fixtures->insertReasons(); + } +} diff --git a/src/Fixtures/AppFixtures.php b/src/Fixtures/AppFixtures.php new file mode 100644 index 00000000..509f9870 --- /dev/null +++ b/src/Fixtures/AppFixtures.php @@ -0,0 +1,645 @@ +dbh = $dbh; + $this->faker = $faker; + $this->bugStatuses = $bugStatuses; + } + + /** + * Insert data in the bugs categories table. + */ + public function insertCategories() + { + $sql = "INSERT INTO bugdb_pseudo_packages ( + `parent`, + `name`, + `long_name`, + `project`, + `list_email`, + `disabled` + ) VALUES ( + :parent, + :name, + :long_name, + :project, + :list_email, + :disabled + ) + "; + + $catIds = []; + + foreach ($this->getCategories() as $category => $data) { + $statement = $this->dbh->prepare($sql); + $parent = isset($data['parent']) ? $catIds[$data['parent']] : 0; + + $statement->execute([ + ':parent' => $parent, + ':name' => $category, + ':long_name' => $data['long_name'] ?? $category, + ':project' => $data['project'] ?? '', + ':list_email' => $data['list_email'] ?? '', + ':disabled' => $data['disabled'] ?? 0, + ]); + + $catIds[$category] = $this->dbh->lastInsertId(); + } + } + + /** + * Insert fixtures in users table. + */ + public function insertBugs() + { + $sql = "INSERT INTO bugdb ( + `package_name`, + `bug_type`, + `email`, + `reporter_name`, + `sdesc`, + `ldesc`, + `php_version`, + `php_os`, + `status`, + `ts1`, + `ts2`, + `assign`, + `passwd`, + `registered`, + `block_user_comment`, + `cve_id`, + `private`, + `visitor_ip` + ) VALUES ( + :package_name, + :bug_type, + :email, + :reporter_name, + :sdesc, + :ldesc, + :php_version, + :php_os, + :status, + :ts1, + :ts2, + :assign, + :passwd, + :registered, + :block_user_comment, + :cve_id, + :private, + INET6_ATON(:visitor_ip) + ) + "; + + $bugs = $this->getBugs(); + + foreach ($bugs as $data) { + $statement = $this->dbh->prepare($sql); + + $statement->execute([ + ':package_name' => $data['package_name'], + ':bug_type' => $data['bug_type'], + ':email' => $data['email'], + ':reporter_name' => $data['reporter_name'], + ':sdesc' => $data['sdesc'], + ':ldesc' => $data['ldesc'], + ':php_version' => $data['php_version'], + ':php_os' => $data['php_os'], + ':status' => $data['status'], + ':ts1' => date('Y-m-d H:i:s'), + ':ts2' => null, + ':assign' => $data['assign'], + ':passwd' => $data['passwd'], + ':registered' => $data['registered'], + ':block_user_comment' => $data['block_user_comment'], + ':cve_id' => $data['cve_id'], + ':private' => $data['private'], + ':visitor_ip' => $data['visitor_ip'], + ]); + } + } + + /** + * Insert data in the bugs reasons table. + */ + public function insertReasons() + { + $sql = "INSERT INTO bugdb_resolves ( + `name`, + `status`, + `title`, + `message`, + `project`, + `package_name`, + `webonly` + ) VALUES ( + :name, + :status, + :title, + :message, + :project, + :package_name, + :webonly + ) + "; + + foreach ($this->getReasons() as $data) { + $statement = $this->dbh->prepare($sql); + + $statement->execute([ + ':name' => $data['name'], + ':status' => $data['status'], + ':title' => $data['title'], + ':message' => $data['message'], + ':project' => $data['project'], + ':package_name' => $data['package_name'], + ':webonly' => $data['webonly'], + ]); + } + } + + /** + * Get generated demo bug reports data with each bug having password set to + * "password". + */ + private function getBugs() + { + $bugs = []; + + // Demo secret password is always password for all bug reports + $password = bugs_get_hash('password'); + + $categories = $this->dbh->query("SELECT id, name FROM bugdb_pseudo_packages")->fetchAll(\PDO::FETCH_KEY_PAIR); + + // More random bug reports + for ($i = 0; $i < self::BUGS_COUNT; $i++) { + $username = $this->faker->unique()->userName; + $username = substr($username, 0, 16); + $username = str_replace('.', '', $username); + + $bugs[] = [ + 'package_name' => $categories[array_rand($categories, 1)], + 'bug_type' => '', + 'email' => $username.'@example.com', + 'reporter_name' => $this->faker->firstName.' '.$this->faker->lastName, + 'sdesc' => $this->faker->text(80), + 'ldesc' => $this->faker->paragraph, + 'php_version' => '7.'.rand(0, 4).'.'.rand(0, 30), + 'php_os' => $this->faker->text(32), + 'status' => array_rand($this->bugStatuses), + 'ts1' => '', + 'ts2' => '', + 'assign' => '', + 'passwd' => $password, + 'registered' => 0, + 'block_user_comment' => 'N', + 'cve_id' => '', + 'private' => 'N', + 'visitor_ip' => $this->faker->ipv4, + ]; + } + + return $bugs; + } + + /** + * Categories. + */ + private function getCategories() + { + return [ + 'General Issues' => [], + 'PDO related' => [], + 'Compile Issues' => [], + 'Configuration Issues' => [], + 'Web Server problem' => [], + 'Calendar problems' => [], + 'Compression related' => [], + 'Directory/Filesystem functions' => [], + 'Database Functions' => [], + 'Data Exchange functions' => [], + 'Extensibility Functions' => [], + 'Graphics related' => [], + 'Languages/Translation' => [], + 'Mail related' => [], + 'Encryption and hash functions' => [], + 'Network functions' => [], + 'PDF functions' => [], + 'Programming Data Structures' => [], + 'Regular expressions' => [], + 'Spelling functions' => [], + 'XML functions' => [], + 'Unicode Issues' => [], + 'Unknown/Other functions' => [], + 'PECL' => [], + 'phpdbg' => [], + 'PHP Language Specification' => [], + + // General Issues + 'Doc Build (PhD) problem' => [ + 'parent' => 'General Issues', + ], + 'Documentation problem' => [ + 'parent' => 'General Issues', + ], + 'Documentation translation problem' => [ + 'parent' => 'General Issues', + ], + 'Filter relate' => [ + 'parent' => 'General Issues', + ], + 'Online Documentation Editor problem' => [ + 'parent' => 'General Issues', + ], + 'Opcache' => [ + 'parent' => 'General Issues', + ], + 'Output Control' => [ + 'parent' => 'General Issues', + ], + 'Performance problem' => [ + 'parent' => 'General Issues', + ], + 'PHAR related' => [ + 'parent' => 'General Issues', + ], + 'PHP-GTK related' => [ + 'parent' => 'General Issues', + ], + 'Systems problem' => [ + 'parent' => 'General Issues', + ], + 'Website problem' => [ + 'parent' => 'General Issues', + ], + 'Reflection related' => [ + 'parent' => 'General Issues', + ], + 'Reproducible crash' => [ + 'parent' => 'General Issues', + ], + 'Scripting Engine problem' => [ + 'parent' => 'General Issues', + ], + 'Session related' => [ + 'parent' => 'General Issues', + ], + 'SPL related' => [ + 'parent' => 'General Issues', + ], + 'Streams related' => [ + 'parent' => 'General Issues', + ], + 'Testing related' => [ + 'parent' => 'General Issues', + ], + + // PDO related + 'PDO Core' => [ + 'parent' => 'PDO related', + ], + 'PDO DBlib' => [ + 'parent' => 'PDO related', + ], + 'PDO Firebird' => [ + 'parent' => 'PDO related', + ], + 'PDO MySQL' => [ + 'parent' => 'PDO related', + ], + 'PDO OCI' => [ + 'parent' => 'PDO related', + ], + 'PDO ODBC' => [ + 'parent' => 'PDO related', + ], + 'PDO PgSQL' => [ + 'parent' => 'PDO related', + ], + 'PDO SQLite' => [ + 'parent' => 'PDO related', + ], + + // Compile Issues + 'Compile Failure' => [ + 'parent' => 'Compile Issues', + ], + 'Compile Warning' => [ + 'parent' => 'Compile Issues', + ], + + // Configuration Issues + 'Dynamic loading' => [ + 'parent' => 'Configuration Issues', + ], + 'PHP options/info functions' => [ + 'parent' => 'Configuration Issues', + ], + 'Safe Mode/open_basedir related' => [ + 'parent' => 'Configuration Issues', + ], + 'Windows Installer related' => [ + 'parent' => 'Configuration Issues', + ], + + // TODO add more + ]; + } + + private function getReasons(): array + { + return [ + [ + 'name' => 'trysnapshot54', + 'status' => 'Feedback', + 'title' => 'Try a snapshot (PHP 5.4)', + 'message' => 'Please try using this snapshot: + + http://snaps.php.net/php5.4-latest.tar.gz + + For Windows: + + http://windows.php.net/snapshots/', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'trysnapshot55', + 'status' => 'Feedback', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'trysnapshottrunk', + 'status' => 'Feedback', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'alreadyfixed', + 'status' => 'Closed', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'needtrace', + 'status' => 'Feedback', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'needscript', + 'status' => 'Feedback', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'oldversion', + 'status' => 'Not a bug', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'support', + 'status' => 'Not a bug', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'nofeedback', + 'status' => 'No Feedback', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 1, + ], + [ + 'name' => 'notwrong', + 'status' => 'Not a bug', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'notenoughinfo', + 'status' => 'Feedback', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'submittedtwice', + 'status' => 'Not a bug', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'globals', + 'status' => 'Not a bug', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'php4', + 'status' => 'Wont fix', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'dst', + 'status' => 'Not a bug', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'isapi', + 'status' => 'Not a bug', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'gnused', + 'status' => 'Not a bug', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'float', + 'status' => 'Not a bug', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'nozend', + 'status' => 'Not a bug', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + [ + 'name' => 'mysqlcfg', + 'status' => 'Not a bug', + 'title' => '', + 'message' => '', + 'project' => 'php', + 'package_name' => '', + 'webonly' => 0, + ], + ]; + } +} diff --git a/tests/Unit/Command/InsertFixturesCommandTest.php b/tests/Unit/Command/InsertFixturesCommandTest.php new file mode 100644 index 00000000..bbbacc6a --- /dev/null +++ b/tests/Unit/Command/InsertFixturesCommandTest.php @@ -0,0 +1,65 @@ +dbh = new \PDO('sqlite::memory:'); + } + + public function testExecute(): void + { + $insertFixturesCommand = new InsertFixturesCommand( + $this->dbh, + new AppFixtures($this->dbh, Factory::create(), []), + 'dev' + ); + + $application = new Application(); + $application->add($insertFixturesCommand); + + $command = $application->find('app:fixtures:insert'); + $commandTester = new CommandTester($command); + $commandTester->setInputs(['n']); + $exitCode = $commandTester->execute(['command' => $command->getName()]); + + $this->assertRegExp('/.../', $commandTester->getDisplay()); + $this->assertSame(0, $exitCode); + } + + public function testExecuteInProdEnv(): void + { + $insertFixturesCommand = new InsertFixturesCommand( + $this->dbh, + new AppFixtures($this->dbh, Factory::create(), []), + 'prod' + ); + + $application = new Application(); + $application->add($insertFixturesCommand); + + $command = $application->find('app:fixtures:insert'); + $commandTester = new CommandTester($command); + $commandTester->setInputs(['n']); + $exitCode = $commandTester->execute(['command' => $command->getName()]); + + $this->assertRegExp('/This command can be executed only in dev/', $commandTester->getDisplay()); + $this->assertSame(0, $exitCode); + } +} From 35a1baa22f5a5388194047182e3eca31371c6b40 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 22 May 2019 03:20:30 +0200 Subject: [PATCH 195/277] Update phpunit schema version --- README.md | 2 +- phpunit.xml.dist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f767e564..af5cc300 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Application unit tests can be executed in development environment after installing dependencies by running `phpunit`: ```bash -phpunit +./vendor/bin/phpunit ``` ## Directory structure diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e19c6612..5bd00609 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -3,7 +3,7 @@ From 49dd23a4a88040cf1134c374b2369a104f803765 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 22 May 2019 04:43:45 +0200 Subject: [PATCH 196/277] Move backtrace pages to templates --- include/functions.php | 26 --- templates/pages/bugs_generating_backtrace.php | 179 ++++++++++++++++ .../pages/bugs_generating_backtrace_win32.php | 105 ++++++++++ www/bugs-generating-backtrace-win32.php | 103 +-------- www/bugs-generating-backtrace.php | 196 +----------------- 5 files changed, 302 insertions(+), 307 deletions(-) create mode 100644 templates/pages/bugs_generating_backtrace.php create mode 100644 templates/pages/bugs_generating_backtrace_win32.php diff --git a/include/functions.php b/include/functions.php index 5e314a29..fd3bbd76 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1707,32 +1707,6 @@ function inline_content_menu($base_url, $current_action, array $menu) echo "

    \n"; } -/** - * Inline content menu for backtraces - * - * Specify either 'Unix' or 'Windows' to select the current page - */ -function backtrace_inline_menu($platform) -{ - if ($platform != 'Unix' && $platform != 'Windows') { - return; - } - - $buffer = ''; - - foreach (['Unix' => '', 'Windows' => '-win32'] as $platform_key => $platform_suffix) { - if ($platform_key == $platform) { - $buffer .= sprintf('%s | ', $platform_key); - } else { - $buffer .= sprintf('%s | ', $platform_suffix, $platform_key); - } - } - - echo "

    "; - echo rtrim($buffer, ' | '); - echo "

    \n"; -} - function admin_table_static(array $header, array $rows) { if (!$header || !$rows || sizeof($header) != sizeof($rows[0])) { diff --git a/templates/pages/bugs_generating_backtrace.php b/templates/pages/bugs_generating_backtrace.php new file mode 100644 index 00000000..b99f3831 --- /dev/null +++ b/templates/pages/bugs_generating_backtrace.php @@ -0,0 +1,179 @@ +extends('layout.php', ['title' => 'Generating a gdb backtrace']) ?> + +start('content') ?> + +

    +Unix | Windows +

    + +

    Generating a gdb backtrace

    + +

    Noticing PHP crashes

    + +There's no absolute way to know that PHP is crashing, but there may be signs. +Typically, if you access a page that is always supposed to generate output (has +a leading HTML block, for example), and suddenly get "Document contains no data" +from your browser, it may mean that PHP crashes somewhere along the execution of +the script. Another way to tell that PHP is crashing is by looking at the Apache +error logs, and looking for SEGV (Apache 1.2) or Segmentation Fault (Apache +1.3). + +

    Important!

    +To get a backtrace with correct information you must have PHP configured with +--enable-debug! + +

    If you don't have a core file yet:

    + +
      +
    • + Remove any limits you may have on core dump size from your shell: +
        +
      • tcsh: unlimit coredumpsize
      • +
      • bash/sh: ulimit -c unlimited
      • +
      +
    • +
    • + Ensure that the directory in which you're running PHP, or the + PHP-enabled httpd, has write permissions for the user who's running PHP. +
    • +
    • + Cause PHP to crash: +
        +
      • PHP CGI: Simply run php with the script that crashes it
      • +
      • PHP Apache Module: Run httpd -X, and access the script that crashes PHP
      • +
      +
    • +
    + +

    Generic way to get a core on Linux

    + +
      +
    • + Set up the core pattern (run this command as root): +
        +
      • echo "<cores dir>/core-%e.%p" > /proc/sys/kernel/core_pattern
      • +
      • make sure the directory is writable by PHP
      • +
      +
    • +
    • Set the ulimit (see above how to do it).
    • +
    • Restart/rerun PHP.
    • +
    + +

    After that any process crashing in your system, including PHP, will leave its +core file in the directory you've specified in core_pattern.

    + +

    Once you have the core file:

    + +
      +
    • + Run gdb with the path to the PHP or PHP-enabled httpd binary, and path + to the core file. Some examples: +
        +
      • gdb /usr/local/apache/sbin/httpd /usr/local/apache/sbin/core
      • +
      • gdb /home/user/dev/php-snaps/sapi/cli/php /home/user/dev/testing/core
      • +
      +
    • +
    • + At the gdb prompt, run: +
        +
      • (gdb) bt
      • +
      +
    • +
    + +

    If you can't get a core file:

    +
      +
    • + Run httpd -X under gdb with something like: +
        +
      • gdb /usr/local/apache/sbin/httpd
      • +
      • (gdb) run -X
      • +
      +
    • +
    • + Then use your web browser and access your server to force the crash. You + should see a gdb prompt appear and some message indicating that there + was a crash. At this gdb prompt, type: +
        +
      • (gdb) bt
      • +
      +
        +
      • + or, running from the commandline +
          +
        • + gdb /home/user/dev/php-snaps/sapi/cli/php +
            +
          • (gdb) run /path/to/script.php
          • +
          • (gdb) bt
          • +
          +
        • +
        +
      • +
      +
    • +
    + +

    This should generate a backtrace, that you should submit in the bug report, +along with any other details you can give us about your setup, and offending +script.

    + +

    Locating which function call caused a segfault:

    + +

    You can locate the function call that caused a segfault, easily, with gdb. +First, you need a core file or to generate a segfault under gdb as described +above.

    + +

    In PHP, each function is executed by an internal function called +execute() and has its own stack. Each line generated by the +bt command represents a function call stack. Typically, you +will see several execute() lines when you issue +bt. You are interested in the last +execute() stack (i.e. smallest frame number). You can move +the current working stack with the up, down or +frame commands. Below is an example gdb session that can be +used as a guideline on how to handle your segfault.

    + +
      +
    • Sample gdb session
    • +
      
      +(gdb) bt
      +#0  0x080ca21b in _efree (ptr=0xbfffdb9b) at zend_alloc.c:240
      +#1  0x080d691a in _zval_dtor (zvalue=0x8186b94) at zend_variables.c:44
      +#2  0x080cfab3 in _zval_ptr_dtor (zval_ptr=0xbfffdbfc) at zend_execute_API.c:274
      +#3  0x080f1cc4 in execute (op_array=0x816c670) at ./zend_execute.c:1605
      +#4  0x080f1e06 in execute (op_array=0x816c530) at ./zend_execute.c:1638
      +#5  0x080f1e06 in execute (op_array=0x816c278) at ./zend_execute.c:1638
      +#6  0x080f1e06 in execute (op_array=0x8166eec) at ./zend_execute.c:1638
      +#7  0x080d7b93 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at zend.c:810
      +#8  0x0805ea75 in php_execute_script (primary_file=0xbffff650) at main.c:1310
      +#9  0x0805cdb3 in main (argc=2, argv=0xbffff6fc) at cgi_main.c:753
      +#10 0x400c91be in __libc_start_main (main=0x805c580 
      , argc=2, ubp_av=0xbffff6fc, + init=0x805b080 <_init>, fini=0x80f67b4 <_fini>, rtld_fini=0x4000ddd0 <_dl_fini>, + stack_end=0xbffff6ec) at ../sysdeps/generic/libc-start.c:129 +(gdb) frame 3 +#3 0x080f1cc4 in execute (op_array=0x816c670) at ./zend_execute.c:1605 +(gdb) print (char *)(executor_globals.function_state_ptr->function)->common.function_name +$14 = 0x80fa6fa "pg_result_error" +(gdb) print (char *)executor_globals.active_op_array->function_name +$15 = 0x816cfc4 "result_error" +(gdb) print (char *)executor_globals.active_op_array->filename +$16 = 0x816afbc "/home/yohgaki/php/DEV/segfault.php" +(gdb) +
      +
    + +

    In this session, frame 3 is the last execute() call. The +frame 3 command moves the current working stack to the +proper frame.
    +print (char *)(executor_globals.function_state_ptr->function)->common.function_name
    +prints the function name. In the sample gdb session, the +pg_result_error() call is causing the segfault. You can print any +internal data that you like, if you know the internal data structure. Please do +not ask how to use gdb or about the internal data structure. Refer to gdb manual +for gdb usage and to the PHP source for the internal data structure.

    + +

    You may not see execute if the segfault happens without +calling any functions.

    + +end('content') ?> diff --git a/templates/pages/bugs_generating_backtrace_win32.php b/templates/pages/bugs_generating_backtrace_win32.php new file mode 100644 index 00000000..985be241 --- /dev/null +++ b/templates/pages/bugs_generating_backtrace_win32.php @@ -0,0 +1,105 @@ +extends('layout.php', ['title' => 'Generating a backtrace on Windows']) ?> + +start('content') ?> + +

    +Unix | Windows +

    + +

    Generating a backtrace, with a compiler, on Windows

    + +

    You'll need to install MS Visual Studio 2008, 2012 or later. You'll also need to

    +
      +
    • either download the debug-pack for your PHP version from windows.php.net/download
    • +
    • or compile your own PHP with --enable-dbg-pack or --enable-debug
    • +
    + +

    If you downloaded the debug-pack from the snaps site, extract it into your +PHP directory and be sure to put the PDB files that belong to the extensions +into your extension directory.

    + +

    If you compile PHP by your own, you can also use a newer version of MSVC.

    + +

    When PHP crashes, click Cancel to debug the process. Now MSVC starts +up in Debug View. If you don't already see the call stack, go into the +View menu and choose Debug WindowsCall Stack.

    + +

    You'll now see something similar to the following lines, this is the backtrace:

    +
    
    +_efree(void * 0x00000000) line 286 + 3 bytes
    +zif_http_test(int 0, _zval_struct * 0x007bc3b0, _zval_struct * * 0x00000000, _zval_struct * 0x00000000, int 0, void * * * 0x00792cd0) line 1685 + 8 bytes
    +zend_do_fcall_common_helper_SPEC(_zend_execute_data * 0x0012fd6c, void * * * 0x00792cd0) line 188 + 95 bytes
    +ZEND_DO_FCALL_SPEC_CONST_HANDLER(_zend_execute_data * 0x0012fd6c, void * * * 0x00792cd0) line 1578 + 13 bytes
    +execute(_zend_op_array * 0x007bc880, void * * * 0x00792cd0) line 88 + 13 bytes
    +zend_eval_string(char * 0x00793bce, _zval_struct * 0x00000000, char * 0x00404588 tring', void * * * 0x00792cd0) line 1056 + 14 bytes
    +zend_eval_string_ex(char * 0x00793bce, _zval_struct * 0x00000000, char * 0x00404588 tring', int 1, void * * * 0x00792cd0) line 1090 + 21 bytes
    +main(int 3, char * * 0x00793ba8) line 1078 + 23 bytes
    +PHP! mainCRTStartup + 227 bytes
    +KERNEL32! 77e81af6()
    +
    + + +

    Generating backtrace, without compiler, on Windows

    + +

    You'll need:

    + + + +

    For the sake of this example, we will simply use PHP in the shell. The same +method can be used for IIS or any other process or services.

    + + +

    Once you have installed the Debug diagnostic tools and uncompressed PHP and +its debug pack (they can be kept in two separate folders), the first step is to +configure the diagnostic tools. Select the tools menu and click on "Options and +settings". The first tab contains the path to the symbols files, add the "debug +folder" to the existing list using the "browse" button:

    + +

    Options

    + +

    Now we are ready to generate our backtrace.

    + +

    We will use the wizard, click the "Add a rule" button and choose "Crash" as +the rule type:

    +

    Wizard #1

    + +

    In the next window, select "a specific process":

    + +

    Wizard #2

    + +

    Add a "sleep(10);" for the first run (from the cmd: "php.exe crashme.php"), +it will let you enough time to click "next" and select the php process. If you +are debugging the Apache module, start Apache with -X option and choose +httpd.exe instead of php.exe from the process list. Then proceed further:

    + +

    Select the php process

    + +

    Click again next and let it crash. If everything went well, you should see +your new rule as shown in the image below:

    + +

    rules list

    + +

    It also detected that "php.exe" was used. A rule has been created for all +instance of "php.exe". It will save you the sleep and process selection.

    + +

    Now you can click the "Analyze data" button:

    + +

    Analyze

    + +

    Et voila, the complete report will show up in your internet explorer +(compressed html):

    + +

    Debug report backtrace screenshot

    + +

    What we need is the backtrace itself which can be found under "Thread X - +System ID XXX".

    + +end('content') ?> diff --git a/www/bugs-generating-backtrace-win32.php b/www/bugs-generating-backtrace-win32.php index a12c26a9..cc09a363 100644 --- a/www/bugs-generating-backtrace-win32.php +++ b/www/bugs-generating-backtrace-win32.php @@ -1,99 +1,14 @@ - -

    Generating a backtrace, with a compiler, on Windows

    - -

    You'll need to install MS Visual Studio 2008, 2012 or later. You'll also need to

    -
      -
    • either download the debug-pack for your PHP version from windows.php.net/download
    • -
    • or compile your own PHP with --enable-dbg-pack or --enable-debug
    • -
    - -

    If you downloaded the debug-pack from the snaps site, extract it into -your PHP directory and be sure to put the PDB files that belong to the -extensions into your extension directory.

    - -

    If you compile PHP by your own, you can also use a newer version of MSVC.

    - -

    When PHP crashes, click Cancel to debug the process. Now MSVC starts -up in Debug View. If you don't already see the call stack, go into the -View menu and choose Debug WindowsCall Stack.

    - -

    You'll now see something similar to the following lines, this is the backtrace:

    -
    
    -_efree(void * 0x00000000) line 286 + 3 bytes
    -zif_http_test(int 0, _zval_struct * 0x007bc3b0, _zval_struct * * 0x00000000, _zval_struct * 0x00000000, int 0, void * * * 0x00792cd0) line 1685 + 8 bytes
    -zend_do_fcall_common_helper_SPEC(_zend_execute_data * 0x0012fd6c, void * * * 0x00792cd0) line 188 + 95 bytes
    -ZEND_DO_FCALL_SPEC_CONST_HANDLER(_zend_execute_data * 0x0012fd6c, void * * * 0x00792cd0) line 1578 + 13 bytes
    -execute(_zend_op_array * 0x007bc880, void * * * 0x00792cd0) line 88 + 13 bytes
    -zend_eval_string(char * 0x00793bce, _zval_struct * 0x00000000, char * 0x00404588 tring', void * * * 0x00792cd0) line 1056 + 14 bytes
    -zend_eval_string_ex(char * 0x00793bce, _zval_struct * 0x00000000, char * 0x00404588 tring', int 1, void * * * 0x00792cd0) line 1090 + 21 bytes
    -main(int 3, char * * 0x00793ba8) line 1078 + 23 bytes
    -PHP! mainCRTStartup + 227 bytes
    -KERNEL32! 77e81af6()
    -
    - - -

    Generating backtrace, without compiler, on Windows

    -

    You'll need:

    - -

    For the sake of this example, we will simply use PHP in the shell. -The same method can be used for IIS or any other process or services.

    - - -

    Once you have installed the Debug diagnostic tools and uncompressed -PHP and its debug pack (they can be kept in two separate folders), the -first step is to configure the diagnostic tools. Select the -tools menu and click on "Options and settings". The first tab contains -the path to the symbols files, add the "debug folder" to the existing -list using the "browse" button:

    -

    Options

    - -

    Now we are ready to generate our backtrace.

    -

    We will use the wizard, click the "Add a rule" button and choose "Crash" as the rule type:

    -

    Wizard #1

    - -

    In the next window, select "a specific process":

    -

    Wizard #2

    - -

    Add a "sleep(10);" for the first run (from the cmd: "php.exe -crashme.php"), it will let you enough time to click "next" and select -the php process. I you are debugging the Apache module, start Apache with -X option and choose -httpd.exe instead of php.exe from the process list. -Then proceed further:

    -

    Select the php process

    - -

    Click again next and let it crash. If everything went well, you should see your new rule as shown in the image below:

    -

    rules list

    - -

    It also detected that "php.exe" was used. A rule has been created -for all instance of "php.exe". It will save you the sleep and process -selection.

    -

    Now you can click the "Analyze data" button:

    -

    Analyze

    - -

    Et voila, the complete report will show up in your internet explorer (compressed html):

    -

    Debug report backtrace screenshot

    - -

    What we need is the backtrace itself which can be found under "Thread X - System ID XXX".

    - -render('pages/bugs_generating_backtrace_win32.php'); diff --git a/www/bugs-generating-backtrace.php b/www/bugs-generating-backtrace.php index 5e6fafcc..4729ec6a 100644 --- a/www/bugs-generating-backtrace.php +++ b/www/bugs-generating-backtrace.php @@ -1,192 +1,14 @@ - -

    Generating a gdb backtrace

    - -

    Noticing PHP crashes

    - -There's no absolute way to know that PHP is crashing, but there may -be signs. Typically, if you access a page that is always supposed -to generate output (has a leading HTML block, for example), and -suddenly get "Document contains no data" from your browser, it may -mean that PHP crashes somewhere along the execution of the script. -Another way to tell that PHP is crashing is by looking at the Apache -error logs, and looking for SEGV (Apache 1.2) or Segmentation -Fault (Apache 1.3). - -

    Important!

    -To get a backtrace with correct information you must have -PHP configured with --enable-debug! - -

    If you don't have a core file yet:

    - -
      -
    • - Remove any limits you may have on core dump size from your shell: -
        -
      • tcsh: unlimit coredumpsize
      • -
      • bash/sh: ulimit -c unlimited
      • -
      -
    • -
    • - Ensure that the directory in which you're running PHP, or the - PHP-enabled httpd, has write permissions for the user who's running PHP. -
    • -
    • - Cause PHP to crash: -
        -
      • PHP CGI: Simply run php with the script that crashes it
      • -
      • PHP Apache Module: Run httpd -X, and access the script that crashes PHP
      • -
      -
    • -
    - -

    Generic way to get a core on Linux

    - -
      -
    • - Set up the core pattern (run this command as root): -
        -
      • echo "<cores dir>/core-%e.%p" > /proc/sys/kernel/core_pattern
      • -
      • make sure the directory is writable by PHP
      • -
      -
    • -
    • - Set the ulimit (see above how to do it). -
    • -
    • - Restart/rerun PHP. -
    • -
    -

    After that any process crashing in your system, including PHP, will leave -its core file in the directory you've specified in core_pattern.

    - -

    Once you have the core file:

    - -
      -
    • - Run gdb with the path to the PHP or PHP-enabled httpd binary, and - path to the core file. Some examples: -
        -
      • gdb /usr/local/apache/sbin/httpd /usr/local/apache/sbin/core
      • -
      • gdb /home/user/dev/php-snaps/sapi/cli/php /home/user/dev/testing/core
      • -
      -
    • -
    • - At the gdb prompt, run: -
        -
      • (gdb) bt
      • -
      -
    • -
    - -

    If you can't get a core file:

    -
      -
    • - Run httpd -X under gdb with something like: -
        -
      • gdb /usr/local/apache/sbin/httpd
      • -
      • (gdb) run -X
      • -
      -
    • -
    • - Then use your web browser and access your server to force the crash. - You should see a gdb prompt appear and some message indicating that - there was a crash. At this gdb prompt, type: -
        -
      • (gdb) bt
      • -
      -
        -
      • - or, running from the commandline -
          -
        • - gdb /home/user/dev/php-snaps/sapi/cli/php -
            -
          • (gdb) run /path/to/script.php
          • -
          • (gdb) bt
          • -
          -
        • -
        -
      • -
      -
    • -
    - -

    This should generate a backtrace, that you should submit in -the bug report, along with any other details you can give us -about your setup, and offending script.

    - -

    Locating which function call caused a segfault:

    - -

    You can locate the function call that caused a segfault, easily, with gdb. -First, you need a core file or to generate a segfault under gdb as described -above.

    - -

    In PHP, each function is executed by an internal function called -execute() and has its own stack. Each line -generated by the bt command represents a function call -stack. Typically, you will see several execute() lines -when you issue bt. You are interested in the last -execute() stack (i.e. smallest frame -number). You can move the current working stack with the -up, down or -frame commands. Below is an example gdb session that can -be used as a guideline on how to handle your segfault.

    - -
      -
    • Sample gdb session
    • -
      
      -(gdb) bt
      -#0  0x080ca21b in _efree (ptr=0xbfffdb9b) at zend_alloc.c:240
      -#1  0x080d691a in _zval_dtor (zvalue=0x8186b94) at zend_variables.c:44
      -#2  0x080cfab3 in _zval_ptr_dtor (zval_ptr=0xbfffdbfc) at zend_execute_API.c:274
      -#3  0x080f1cc4 in execute (op_array=0x816c670) at ./zend_execute.c:1605
      -#4  0x080f1e06 in execute (op_array=0x816c530) at ./zend_execute.c:1638
      -#5  0x080f1e06 in execute (op_array=0x816c278) at ./zend_execute.c:1638
      -#6  0x080f1e06 in execute (op_array=0x8166eec) at ./zend_execute.c:1638
      -#7  0x080d7b93 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at zend.c:810
      -#8  0x0805ea75 in php_execute_script (primary_file=0xbffff650) at main.c:1310
      -#9  0x0805cdb3 in main (argc=2, argv=0xbffff6fc) at cgi_main.c:753
      -#10 0x400c91be in __libc_start_main (main=0x805c580 
      , argc=2, ubp_av=0xbffff6fc, - init=0x805b080 <_init>, fini=0x80f67b4 <_fini>, rtld_fini=0x4000ddd0 <_dl_fini>, - stack_end=0xbffff6ec) at ../sysdeps/generic/libc-start.c:129 -(gdb) frame 3 -#3 0x080f1cc4 in execute (op_array=0x816c670) at ./zend_execute.c:1605 -(gdb) print (char *)(executor_globals.function_state_ptr->function)->common.function_name -$14 = 0x80fa6fa "pg_result_error" -(gdb) print (char *)executor_globals.active_op_array->function_name -$15 = 0x816cfc4 "result_error" -(gdb) print (char *)executor_globals.active_op_array->filename -$16 = 0x816afbc "/home/yohgaki/php/DEV/segfault.php" -(gdb) -
      -
    - -

    In this session, frame 3 is the last execute() -call. The frame 3 command moves the current working stack -to the proper frame.
    -print (char *)(executor_globals.function_state_ptr->function)->common.function_name
    -prints the function name. In the sample gdb session, the -pg_result_error() call -is causing the segfault. You can print any internal data that you like, -if you know the internal data structure. Please do not ask how to use gdb -or about the internal data structure. Refer to gdb manual for gdb usage -and to the PHP source for the internal data structure.

    - -

    You may not see execute if the segfault happens -without calling any functions.

    - -render('pages/bugs_generating_backtrace.php'); From 03e280e9bcc6fb964e6c7b8fc9e54ab93d6b14f4 Mon Sep 17 00:00:00 2001 From: Pieter Hordijk Date: Wed, 22 May 2019 16:45:17 +0300 Subject: [PATCH 197/277] Use noHtml() instead of e() to escape data in href attribute --- templates/pages/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/pages/index.php b/templates/pages/index.php index 177e93f2..dedb98b0 100644 --- a/templates/pages/index.php +++ b/templates/pages/index.php @@ -60,7 +60,7 @@ From fe351f79ddc962b22235f00e66a13d8b4f4034f5 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 22 May 2019 20:12:38 +0200 Subject: [PATCH 198/277] Move quick fix reasons list to template --- templates/pages/quick_fix_desc.php | 29 ++++++++++++++++ www/quick-fix-desc.php | 53 ++++++++---------------------- 2 files changed, 43 insertions(+), 39 deletions(-) create mode 100644 templates/pages/quick_fix_desc.php diff --git a/templates/pages/quick_fix_desc.php b/templates/pages/quick_fix_desc.php new file mode 100644 index 00000000..608cb8ba --- /dev/null +++ b/templates/pages/quick_fix_desc.php @@ -0,0 +1,29 @@ +extends('layout.php', ['title' => 'Quick fix descriptions']) ?> + +start('content') ?> + + + $reason): ?> + + + + + + + + + + + + $variation): ?> + + + + + + + + +
    e($reason['title']) ?>Status: e($reason['status']) ?>
    e($reason['message']) ?>
    e($reason['title']) ?> (e($type) ?>)Status: e($reason['status']) ?>
    e($variation) ?>
    + +end('content') ?> diff --git a/www/quick-fix-desc.php b/www/quick-fix-desc.php index c360379d..5aad03fa 100644 --- a/www/quick-fix-desc.php +++ b/www/quick-fix-desc.php @@ -1,47 +1,22 @@ get(ReasonRepository::class); -list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($site); - -// Authenticate -bugs_authenticate($user, $pw, $logged_in, $user_flags); - -response_header('Quick Fix Descriptions'); - -?> - - $reason) { - if (!empty($reason['package_name'])) - $reason['title'] = "{$reason['title']} ({$reason['package_name']})"; - - echo " - - - - - - "; - if (isset($FIX_VARIATIONS[$key])) { - foreach ($FIX_VARIATIONS[$key] as $type => $variation) { - echo " - - - - - "; - } - } -} -?> -
    {$reason['title']}Status: {$reason['status']}
    {$reason['message']}
    {$reason['title']} ({$type})Status: {$reason['status']}
    {$variation}
    +list($reasons, $variations) = $reasonRepository->findByProject('php'); -render('pages/quick_fix_desc.php', [ + 'reasons' => $reasons, + 'variations' => $variations, +]); From e8bab3538a9d05d0da584c2ff8b18c7d9eea2fdd Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Thu, 23 May 2019 02:10:03 +0200 Subject: [PATCH 199/277] Add variables import to included templates --- docs/templates.md | 9 +++++++-- src/Template/Context.php | 8 +++++++- tests/Unit/Template/ContextTest.php | 8 ++++++++ tests/fixtures/templates/forms/form_2.php | 10 ++++++++++ tests/fixtures/templates/pages/including.php | 3 ++- 5 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/templates/forms/form_2.php diff --git a/docs/templates.md b/docs/templates.md index cea0976e..0623d91e 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -73,8 +73,13 @@ To include a partial template snippet file: include('forms/report_bug.php') ?> ``` -which is equivalent to ``. -The variable scope is inherited by the template that included the file. +which is equivalent to ``, +except that the variable scope is not inherited by the template that included +the file. To import variables into the included template snippet file: + +```php +include('forms/contact.php', ['formHeading' => 'value', 'foo' => 'bar']) ?> +``` ## Blocks diff --git a/src/Template/Context.php b/src/Template/Context.php index eaf94032..98994f44 100644 --- a/src/Template/Context.php +++ b/src/Template/Context.php @@ -141,8 +141,14 @@ public function end(string $name): void * * @return mixed */ - public function include(string $template) + public function include(string $template, array $variables = []) { + if (count($variables) > extract($variables, EXTR_SKIP)) { + throw new \Exception( + 'Variables with numeric names $0, $1... cannot be imported to scope '.$template + ); + } + return include $this->dir.'/'.$template; } diff --git a/tests/Unit/Template/ContextTest.php b/tests/Unit/Template/ContextTest.php index 8b82de72..c4d22523 100644 --- a/tests/Unit/Template/ContextTest.php +++ b/tests/Unit/Template/ContextTest.php @@ -52,6 +52,14 @@ public function testIncludeReturn(): void $this->assertEquals(include TEST_FIXTURES_DIRECTORY . '/templates/includes/variable.php', $variable); } + public function testIncludeOnInvalidVariableCounts(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Variables with numeric names $0, $1... cannot be imported to scope includes/variable.php'); + + $this->context->include('includes/variable.php', ['var1', 'var2', 'var3']); + } + /** * @dataProvider attacksProvider */ diff --git a/tests/fixtures/templates/forms/form_2.php b/tests/fixtures/templates/forms/form_2.php new file mode 100644 index 00000000..ba4a660b --- /dev/null +++ b/tests/fixtures/templates/forms/form_2.php @@ -0,0 +1,10 @@ +append('scripts'); ?> + +end('scripts'); ?> + +e($importedVariable) ?> + +
    + + +
    diff --git a/tests/fixtures/templates/pages/including.php b/tests/fixtures/templates/pages/including.php index 746b59a4..fd157bda 100644 --- a/tests/fixtures/templates/pages/including.php +++ b/tests/fixtures/templates/pages/including.php @@ -1,5 +1,6 @@ extends('layout.php', ['title' => 'Testing blocks appends']) ?> start('content') ?> -include('forms/form.php') ?> + +include('forms/form_2.php', ['importedVariable' => $someVariable]) ?> end('content') ?> From 9531cd154db3a5e41b6e806226e9e371d2699f52 Mon Sep 17 00:00:00 2001 From: Pieter Hordijk Date: Wed, 22 May 2019 21:06:46 +0300 Subject: [PATCH 200/277] Converted the current admin pages (/admin) to use proper templates and repositories --- config/container.php | 8 ++ src/Repository/DatabaseStatusRepository.php | 60 +++++++++ src/Repository/PhpInfoRepository.php | 46 +++++++ src/Repository/ReasonRepository.php | 10 ++ templates/pages/admin/database_status.php | 75 +++++++++++ templates/pages/admin/mailing_lists.php | 18 +++ templates/pages/admin/menu.php | 17 +++ templates/pages/admin/phpinfo.php | 9 ++ templates/pages/admin/quick_responses.php | 38 ++++++ www/admin/index.php | 139 ++++++-------------- www/css/style.css | 7 + 11 files changed, 327 insertions(+), 100 deletions(-) create mode 100644 src/Repository/DatabaseStatusRepository.php create mode 100644 src/Repository/PhpInfoRepository.php create mode 100644 templates/pages/admin/database_status.php create mode 100644 templates/pages/admin/mailing_lists.php create mode 100644 templates/pages/admin/menu.php create mode 100644 templates/pages/admin/phpinfo.php create mode 100644 templates/pages/admin/quick_responses.php diff --git a/config/container.php b/config/container.php index 59e30457..d869f446 100644 --- a/config/container.php +++ b/config/container.php @@ -33,6 +33,10 @@ return new App\Repository\CommentRepository($c->get(\PDO::class)); }); +$container->set(App\Repository\DatabaseStatusRepository::class, function ($c) { + return new App\Repository\DatabaseStatusRepository($c->get(\PDO::class)); +}); + $container->set(App\Repository\ObsoletePatchRepository::class, function ($c) { return new App\Repository\ObsoletePatchRepository($c->get(\PDO::class)); }); @@ -45,6 +49,10 @@ return new App\Repository\PatchRepository($c->get(\PDO::class), $c->get('uploads_dir')); }); +$container->set(App\Repository\PhpInfoRepository::class, function ($c) { + return new App\Repository\PhpInfoRepository(); +}); + $container->set(App\Repository\PullRequestRepository::class, function ($c) { return new App\Repository\PullRequestRepository($c->get(\PDO::class)); }); diff --git a/src/Repository/DatabaseStatusRepository.php b/src/Repository/DatabaseStatusRepository.php new file mode 100644 index 00000000..971ba424 --- /dev/null +++ b/src/Repository/DatabaseStatusRepository.php @@ -0,0 +1,60 @@ +dbh = $dbh; + } + + public function getMysqlVersion(): string + { + return $this->dbh->query('SELECT version() mysql_version')->fetchColumn(0); + } + + /** + * @return string[] + */ + public function findAllTables(): array + { + return $this->dbh->query('SHOW TABLES')->fetchAll(\PDO::FETCH_COLUMN); + } + + /** + * @return array + */ + public function getNumberOfRowsInTables(): array + { + $numberOfRowsPerTable = []; + + foreach ($this->findAllTables() as $tableName) { + $sql = sprintf('SELECT COUNT(*) FROM `%s`', $tableName); + + $numberOfRowsPerTable[$tableName] = $this->dbh->query($sql)->fetchColumn(0); + } + + return $numberOfRowsPerTable; + } + + /** + * @return array> + */ + public function getStatusOfTables(): array + { + return $this->dbh->query('SHOW TABLE STATUS')->fetchAll(); + } +} diff --git a/src/Repository/PhpInfoRepository.php b/src/Repository/PhpInfoRepository.php new file mode 100644 index 00000000..65423570 --- /dev/null +++ b/src/Repository/PhpInfoRepository.php @@ -0,0 +1,46 @@ +replaceSensitiveInformation(ob_get_clean()); + $phpInfo = $this->cleanHtml($phpInfo); + + return $phpInfo; + } + + private function replaceSensitiveInformation(string $phpInfo): string + { + return str_replace([ + getenv('AUTH_TOKEN'), + getenv('USER_TOKEN'), + getenv('USER_PWD_SALT'), + ], '<hidden>', $phpInfo); + } + + /** + * This method unwrap the information from the body tag, removes zend and php logos and fixes sizes + * for presentation purposes + * + * We might want to do it proper at some point using DOM methods + */ + private function cleanHtml(string $phpInfo): string + { + preg_match('!(.*)!ims', $phpInfo, $m); + + $m[1] = preg_replace('!!ims', '', $m[1]); + $m[1] = preg_replace('!!ims', '', $m[1]); + $m[1] = str_replace(' width="600"', ' width="80%"', $m[1]); + + return $m[1]; + } +} diff --git a/src/Repository/ReasonRepository.php b/src/Repository/ReasonRepository.php index fd4f411c..b33d60a6 100644 --- a/src/Repository/ReasonRepository.php +++ b/src/Repository/ReasonRepository.php @@ -52,4 +52,14 @@ public function findByProject(string $project = ''): array return [$resolves, $variations]; } + + /** + * @return array> + */ + public function findAll(): array + { + $sql = 'SELECT * FROM bugdb_resolves'; + + return $this->dbh->query($sql)->fetchAll(); + } } diff --git a/templates/pages/admin/database_status.php b/templates/pages/admin/database_status.php new file mode 100644 index 00000000..a1237cee --- /dev/null +++ b/templates/pages/admin/database_status.php @@ -0,0 +1,75 @@ +extends('layout.php', ['title' => 'Admin :: Database status']) ?> + +start('content') ?> + +include('pages/admin/menu.php', ['action' => $action]); ?> + +

    Running MySQL e($mysqlVersion); ?>

    + +

    Number of rows:

    + + + + + + + + $numberOfRows): ?> + + + + + + +
    TableRows
    e($tableName); ?>e($numberOfRows); ?>
    + +

    Table status:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameEngineVersionRow_formatRowsAvg_row_lengthData_lengthMax_data_lengthIndex_lengthData_freeAuto_incrementCreate_timeUpdate_timeCheck_timeCollationChecksumCreate_optionsComment
    e($tableStatus['Name']); ?>e($tableStatus['Engine']); ?>e($tableStatus['Row_format']); ?>e($tableStatus['Create_time']); ?>e($tableStatus['Update_time']); ?>e($tableStatus['Check_time']) : ''; ?>e($tableStatus['Collation']); ?>e($tableStatus['Checksum']) : ''; ?>e($tableStatus['Create_options']); ?>e($tableStatus['Comment']); ?>
    + +end('content') ?> diff --git a/templates/pages/admin/mailing_lists.php b/templates/pages/admin/mailing_lists.php new file mode 100644 index 00000000..e0e307d4 --- /dev/null +++ b/templates/pages/admin/mailing_lists.php @@ -0,0 +1,18 @@ +extends('layout.php', ['title' => 'Admin :: Package mailing lists']) ?> + +start('content') ?> + +include('pages/admin/menu.php', ['action' => $action]); ?> + +
    + +
    e($list['name']); ?>:
    +
    + + e($list['list_email']); ?> + +
    + +
    + +end('content') ?> diff --git a/templates/pages/admin/menu.php b/templates/pages/admin/menu.php new file mode 100644 index 00000000..7d716f7e --- /dev/null +++ b/templates/pages/admin/menu.php @@ -0,0 +1,17 @@ +

    + > + phpinfo() + + | + > + Package mailing lists + + | + > + Quick fix responses + + | + > + Database status + +

    diff --git a/templates/pages/admin/phpinfo.php b/templates/pages/admin/phpinfo.php new file mode 100644 index 00000000..c04b33e3 --- /dev/null +++ b/templates/pages/admin/phpinfo.php @@ -0,0 +1,9 @@ +extends('layout.php', ['title' => 'Admin :: phpinfo()']) ?> + +start('content') ?> + +include('pages/admin/menu.php', ['action' => $action]); ?> + + + +end('content') ?> diff --git a/templates/pages/admin/quick_responses.php b/templates/pages/admin/quick_responses.php new file mode 100644 index 00000000..64dde7a2 --- /dev/null +++ b/templates/pages/admin/quick_responses.php @@ -0,0 +1,38 @@ +extends('layout.php', ['title' => 'Admin :: Quick fix responses']) ?> + +start('content') ?> + +include('pages/admin/menu.php', ['action' => $action]); ?> + +

    List Responses

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    idnamestatustitlemessageprojectpackage_namewebonly
    e($response['name']); ?>e($response['status']) : ''; ?>e($response['title']); ?> + e($response['message'])); ?> + e($response['project']); ?>e($response['package_name']) : ''; ?>
    + +end('content') ?> diff --git a/www/admin/index.php b/www/admin/index.php index 3d3affd2..43dd0b6f 100644 --- a/www/admin/index.php +++ b/www/admin/index.php @@ -1,11 +1,15 @@ 'phpinfo()', - 'list_lists' => 'Package mailing lists', - 'list_responses' => 'Quick fix responses', - 'mysql' => 'Database status', -]; - -$action = !empty($_GET['action']) && isset($actions[$_GET['action']]) ? $_GET['action'] : 'list_lists'; - -response_header("Bugs admin suite"); -inline_content_menu('/admin/', $action, $actions); - -if ($action === 'phpinfo') { - ob_start(); - phpinfo(); - - $phpinfo = ob_get_clean(); - - // Attempt to hide certain ENV vars - $vars = [ - getenv('AUTH_TOKEN'), - getenv('USER_TOKEN'), - getenv('USER_PWD_SALT') - ]; - - $phpinfo = str_replace($vars, '<hidden>', $phpinfo); - - // Semi stolen from php-web - $m = []; - - preg_match('!(.*)!ims', $phpinfo, $m); - - $m[1] = preg_replace('!!ims', '', $m[1]); - $m[1] = preg_replace('!!ims', '', $m[1]); - $m[1] = str_replace(' width="600"', ' width="80%"', $m[1]); - - echo $m[1]; - -} elseif ($action === 'list_lists') { - echo "
    \n"; - foreach ($container->get(PackageRepository::class)->findLists() as $row) { - echo "
    ", $row['name'], ":
    \n
    ", mailto_list(explode(',', $row['list_email'])), "
    \n"; - } - echo "
    \n"; -} elseif ($action === 'list_responses') { - - $res = $dbh->query(" - SELECT * - FROM bugdb_resolves - "); - - echo "

    List Responses

    \n"; - - $rows = []; - while ($row = $res->fetch()) { - // This is ugly but works (tm) - $row['message'] = nl2br($row['message']); - - $rows[] = $row; - } - - admin_table_dynamic($rows); -} elseif ($action === 'mysql') { - $res = $dbh->query("SHOW TABLES"); - - $sql = "SELECT version() mysql_version\n"; - - while ($row = $res->fetch(\PDO::FETCH_NUM)) { - $table = $row[0]; - $sql .= "\t, (SELECT COUNT(*) FROM `$table`) `cnt_$table`\n"; - } - - $res = $dbh->query($sql); - $row = $res->fetch(); - - echo "

    Running MySQL ".$row['mysql_version']."

    "; - unset($row['mysql_version']); - - echo "

    Number of rows:

    \n"; - - $rows = []; - - foreach($row as $key => $value) { - $rows[] = [str_replace('cnt_', '', $key), $value]; - } - - admin_table_static(['Table', 'Rows'], $rows); - - $rows = []; - $res = $dbh->query("SHOW TABLE STATUS"); - echo "

    Table status:

    \n"; - while ($row = $res->fetch()) { - $rows[] = $row; - } - - admin_table_dynamic($rows); +$action = $_GET['action'] ?? 'list_lists'; + +switch ($action) { + case 'phpinfo': + echo $template->render('pages/admin/phpinfo.php', [ + 'action' => $action, + 'info' => $container->get(PhpInfoRepository::class)->getInfo(), + ]); + break; + + case 'list_responses': + echo $template->render('pages/admin/quick_responses.php', [ + 'action' => $action, + 'responses' => $container->get(ReasonRepository::class)->findAll(), + ]); + break; + + case 'mysql': + echo $template->render('pages/admin/database_status.php', [ + 'action' => $action, + 'mysqlVersion' => $container->get(DatabaseStatusRepository::class)->getMysqlVersion(), + 'numberOfRowsPerTable' => $container->get(DatabaseStatusRepository::class)->getNumberOfRowsInTables(), + 'statusPerTable' => $container->get(DatabaseStatusRepository::class)->getStatusOfTables(), + ]); + break; + + case 'list_lists': + default: + echo $template->render('pages/admin/mailing_lists.php', [ + 'action' => $action, + 'lists' => $container->get(PackageRepository::class)->findLists(), + ]); + break; } - -response_footer(); diff --git a/www/css/style.css b/www/css/style.css index a9b07b58..743ea143 100644 --- a/www/css/style.css +++ b/www/css/style.css @@ -676,6 +676,13 @@ td.tbl-row-message { text-align: left; } +/* menu */ +a.active { + font-weight: bold; + color: black; + text-decoration: none; +} + /* phpinfo() */ div.center { margin: 0px auto; From 95cdd55d51122c8ba26f6a9db7decfc1682a40cd Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Thu, 23 May 2019 03:00:42 +0200 Subject: [PATCH 201/277] Remove unused functions - admin_table_static - admin_table_dynamic - inline_content_menu --- include/functions.php | 114 ------------------------------------------ 1 file changed, 114 deletions(-) diff --git a/include/functions.php b/include/functions.php index fd3bbd76..a4e3a0c9 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1677,117 +1677,3 @@ function bugs_get_hash($passwd) { return hash_hmac('sha256', $passwd, getenv('USER_PWD_SALT')); } - - -/** - * Inline content menu - * - * . The base_url is a prefix for the location, currently does not support _SERVER['QUERY_STRING'] - * . The current_action is used to indicate which page is currently being displayed - * . Menu items are in (action_name => Title) array pairs - */ -function inline_content_menu($base_url, $current_action, array $menu) -{ - if (!$menu) { - return; - } - - $buffer = ''; - - foreach ($menu as $action => $title) { - if ($current_action === $action) { - $buffer .= sprintf('%s | ', $title); - } else { - $buffer .= sprintf('%s | ', $base_url, $action, $title); - } - } - - echo "

    "; - echo rtrim($buffer, ' | '); - echo "

    \n"; -} - -function admin_table_static(array $header, array $rows) -{ - if (!$header || !$rows || sizeof($header) != sizeof($rows[0])) { - return; - } - - echo "\n"; - echo "\n"; - - foreach ($header as $name) { - echo "\n"; - } - - echo "\n"; - - - foreach ($rows as $row) { - $i = 0; - - echo "\n"; - - foreach ($row as $value) { - echo "\n"; - - ++$i; - } - - echo "\n"; - } - - echo "
    $name
    $value
    \n"; -} - -function admin_table_dynamic(array $rows) -{ - if (!$rows) { - return; - } - - $printed_header = false; - - echo "\n"; - - foreach ($rows as $row) { - if (!$printed_header) { - echo "\n"; - - foreach (array_keys($row) as $column) { - echo "\n"; - } - - echo "\n"; - - $printed_header = true; - } - - $i = 0; - - echo "\n"; - - foreach ($row as $column => $value) { - echo "\n"; - - ++$i; - } - - echo "\n"; - } -} - -function mailto_list(array $mails) -{ - if(!$mails) { - return; - } - - $buffer = ''; - - foreach ($mails as $mail) { - $buffer .= sprintf('%1$s, ', $mail); - } - - echo rtrim($buffer, ', '); -} From fd1338e3de54fd96b6cdaeb08508b4a5ba84f67a Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sat, 25 May 2019 12:16:43 +0200 Subject: [PATCH 202/277] Revert "Add automatic fixtures generator importer" This reverts commit 5cdac95adc49562291a873c8704de3ceb0d9d398. Due to the https://github.com/php/web-bugs/pull/77 discussion and request from a prominent member of the PHP group - Kalle - I'm reverting this awesome addition because it caused too many conflicts and issues. So it is not worth of investing more from my side into this neither having a ruined experience further on from any side here. This fix has been really helpful with some development ways but people who maintain this app further better have peace of mind... --- .gitignore | 2 +- README.md | 12 +- composer.json | 4 +- composer.lock | 254 +------ config/container.php | 21 - config/parameters.php | 20 - scripts/console | 15 - sql/fixtures.sql | 4 + src/Command/InsertFixturesCommand.php | 100 --- src/Fixtures/AppFixtures.php | 645 ------------------ .../Command/InsertFixturesCommandTest.php | 65 -- 11 files changed, 10 insertions(+), 1132 deletions(-) delete mode 100755 scripts/console create mode 100644 sql/fixtures.sql delete mode 100644 src/Command/InsertFixturesCommand.php delete mode 100644 src/Fixtures/AppFixtures.php delete mode 100644 tests/Unit/Command/InsertFixturesCommandTest.php diff --git a/.gitignore b/.gitignore index fe3c3ebd..8d0441ea 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ # Local specific PHPUnit configuration /phpunit.xml -/.phpunit.result.cache +.phpunit.result.cache # Transient and temporary generated files /var/ diff --git a/README.md b/README.md index af5cc300..1adab68d 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,8 @@ Modify `local_config.php` according to your local development environment. * Database: -Create a new MySQL, MariaDB, Percona Server, or equivalent database using -`sql/database.sql`, create database schema `sql/schema.sql` and insert demo data -fixtures by running: - -```bash -php scripts/console app:fixtures:insert -``` +Create a new MySQL/MariaDB database using `sql/database.sql`, create database +schema `sql/schema.sql` and insert fixtures using `sql/fixtures.sql`. ## Tests @@ -47,10 +42,9 @@ Source code of this application is structured in the following directories: ├─ prepend.php # Autoloader, DB connection, container, app initialization └─ ... └─ scripts/ # Command line development tools and scripts - ├─ console # Application's main script for running CLI commands ├─ cron/ # Various systems scripts to run periodically on the server └─ ... - ├─ sql/ # Database schema + ├─ sql/ # Database schema and fixtures └─ src/ # Application source code classes ├─ Horde/ # https://www.horde.org/libraries/Horde_Text_Diff └─ ... diff --git a/composer.json b/composer.json index 4d022655..abec91de 100644 --- a/composer.json +++ b/composer.json @@ -32,9 +32,7 @@ }, "require-dev": { "ext-pdo_sqlite": "*", - "fzaninotto/faker": "^1.8", - "phpunit/phpunit": "^8.1.5", - "symfony/console": "^4.2" + "phpunit/phpunit": "^8.1.5" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 898739c0..81116187 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e90209efe832cd89e50a5f742fd551a8", + "content-hash": "994d11b989a10210dc468c7046539905", "packages": [], "packages-dev": [ { @@ -63,56 +63,6 @@ ], "time": "2019-03-17T17:37:11+00:00" }, - { - "name": "fzaninotto/faker", - "version": "v1.8.0", - "source": { - "type": "git", - "url": "https://github.com/fzaninotto/Faker.git", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "ext-intl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7", - "squizlabs/php_codesniffer": "^1.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } - }, - "autoload": { - "psr-4": { - "Faker\\": "src/Faker/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "François Zaninotto" - } - ], - "description": "Faker is a PHP library that generates fake data for you.", - "keywords": [ - "data", - "faker", - "fixtures" - ], - "time": "2018-07-12T10:23:15+00:00" - }, { "name": "myclabs/deep-copy", "version": "1.9.1", @@ -1381,149 +1331,6 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2016-10-03T07:35:21+00:00" }, - { - "name": "symfony/console", - "version": "v4.2.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "e2840bb38bddad7a0feaf85931e38fdcffdb2f81" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/e2840bb38bddad7a0feaf85931e38fdcffdb2f81", - "reference": "e2840bb38bddad7a0feaf85931e38fdcffdb2f81", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/contracts": "^1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com/", - "time": "2019-04-08T14:23:48+00:00" - }, - { - "name": "symfony/contracts", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/contracts.git", - "reference": "d3636025e8253c6144358ec0a62773cae588395b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/contracts/zipball/d3636025e8253c6144358ec0a62773cae588395b", - "reference": "d3636025e8253c6144358ec0a62773cae588395b", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "require-dev": { - "psr/cache": "^1.0", - "psr/container": "^1.0", - "symfony/polyfill-intl-idn": "^1.10" - }, - "suggest": { - "psr/cache": "When using the Cache contracts", - "psr/container": "When using the Service contracts", - "symfony/cache-contracts-implementation": "", - "symfony/event-dispatcher-implementation": "", - "symfony/http-client-contracts-implementation": "", - "symfony/service-contracts-implementation": "", - "symfony/translation-contracts-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\": "" - }, - "exclude-from-classmap": [ - "**/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A set of abstractions extracted out of the Symfony components", - "homepage": "https://symfony.com/", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "time": "2019-04-27T14:29:50+00:00" - }, { "name": "symfony/polyfill-ctype", "version": "v1.11.0", @@ -1582,65 +1389,6 @@ ], "time": "2019-02-06T07:57:58+00:00" }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.11.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com/", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2019-02-06T07:57:58+00:00" - }, { "name": "theseer/tokenizer", "version": "1.1.2", diff --git a/config/container.php b/config/container.php index d869f446..11dc4ae3 100644 --- a/config/container.php +++ b/config/container.php @@ -89,25 +89,4 @@ return new App\Utils\Uploader(); }); -$container->set(App\Fixtures\AppFixtures::class, function ($c) { - return new App\Fixtures\AppFixtures($c->get(\PDO::class), Faker\Factory::create(), $c->get('bug_statuses')); -}); - -$container->set(App\Command\InsertFixturesCommand::class, function ($c) { - $insertFixturesCommand = new App\Command\InsertFixturesCommand( - $c->get(\PDO::class), - $c->get(App\Fixtures\AppFixtures::class), - $c->get('env') - ); - - return $insertFixturesCommand; -}); - -$container->set(Symfony\Component\Console\Application::class, function ($c) { - $application = new Symfony\Component\Console\Application(); - $application->add($c->get(App\Command\InsertFixturesCommand::class)); - - return $application; -}); - return $container; diff --git a/config/parameters.php b/config/parameters.php index 0cf1b723..9ff0d78a 100644 --- a/config/parameters.php +++ b/config/parameters.php @@ -70,24 +70,4 @@ * Templates directory. */ 'templates_dir' => __DIR__.'/../templates', - - /** - * Bug statuses. - */ - 'bug_statuses' => [ - 'Open' => 'Opn', - 'Not a bug' => 'Nab', - 'Feedback' => 'Fbk', - 'No Feedback' => 'NoF', - 'Wont fix' => 'Wfx', - 'Duplicate' => 'Dup', - 'Critical' => 'Ctl', - 'Assigned' => 'Asn', - 'Analyzed' => 'Ana', - 'Verified' => 'Ver', - 'Suspended' => 'Sus', - 'Closed' => 'Csd', - 'Spam' => 'Spm', - 'Re-Opened' => 'ReO', - ], ]; diff --git a/scripts/console b/scripts/console deleted file mode 100755 index 6c2f44f0..00000000 --- a/scripts/console +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env php -] [arguments] - */ - -require_once __DIR__.'/../include/prepend.php'; - -use Symfony\Component\Console\Application; - -$container->get(Application::class)->run(); diff --git a/sql/fixtures.sql b/sql/fixtures.sql new file mode 100644 index 00000000..c4f3bf79 --- /dev/null +++ b/sql/fixtures.sql @@ -0,0 +1,4 @@ +-- Default pseudo packages (common for all projects) + +INSERT INTO bugdb_pseudo_packages SET id = '1', parent = '0', name = 'Web Site', long_name = 'Web Site', project = ''; +INSERT INTO bugdb_pseudo_packages SET id = '2', parent = '1', name = 'Bug System', long_name = 'Bug System', project = ''; diff --git a/src/Command/InsertFixturesCommand.php b/src/Command/InsertFixturesCommand.php deleted file mode 100644 index b833f2ce..00000000 --- a/src/Command/InsertFixturesCommand.php +++ /dev/null @@ -1,100 +0,0 @@ -dbh = $dbh; - $this->fixtures = $fixtures; - $this->environment = $environment; - - parent::__construct(); - } - - /** - * Configure command. - */ - protected function configure() - { - $this->setName('app:fixtures:insert') - ->setDescription('Insert database fixtures.') - ->setHelp('This command inserts demo data fixtures in the database.') - ; - } - - /** - * Run the command, for example, bin/console app:generate-fixtures. It can - * be executed only in development environment as a safety measure to not - * delete something important. - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - if ('dev' !== $this->environment) { - $output->writeln('This command can be executed only in development.'); - - return; - } - - $helper = $this->getHelper('question'); - $question = new ConfirmationQuestion( - 'This will erase entire database. Are you sure you want to continue? [y/N]', - false - ); - - if (!$helper->ask($input, $output, $question)) { - $output->writeln('Exiting...'); - - return; - } - - // Delete all existing categories and add new ones. - $output->writeln('Deleting existing categories'); - $this->dbh->query('DELETE FROM bugdb_pseudo_packages'); - $output->writeln('Adding new categories'); - $this->fixtures->insertCategories(); - - // Delete all current bug reports and add new ones. - $output->writeln('Deleting existing bug reports'); - $this->dbh->query('DELETE FROM bugdb'); - $output->writeln('Adding new bug reports'); - $this->fixtures->insertBugs(); - - // Delete all current reasons and add new ones. - $output->writeln('Deleting existing reasons'); - $this->dbh->query('DELETE FROM bugdb_resolves'); - $output->writeln('Adding new reasons'); - $this->fixtures->insertReasons(); - } -} diff --git a/src/Fixtures/AppFixtures.php b/src/Fixtures/AppFixtures.php deleted file mode 100644 index 509f9870..00000000 --- a/src/Fixtures/AppFixtures.php +++ /dev/null @@ -1,645 +0,0 @@ -dbh = $dbh; - $this->faker = $faker; - $this->bugStatuses = $bugStatuses; - } - - /** - * Insert data in the bugs categories table. - */ - public function insertCategories() - { - $sql = "INSERT INTO bugdb_pseudo_packages ( - `parent`, - `name`, - `long_name`, - `project`, - `list_email`, - `disabled` - ) VALUES ( - :parent, - :name, - :long_name, - :project, - :list_email, - :disabled - ) - "; - - $catIds = []; - - foreach ($this->getCategories() as $category => $data) { - $statement = $this->dbh->prepare($sql); - $parent = isset($data['parent']) ? $catIds[$data['parent']] : 0; - - $statement->execute([ - ':parent' => $parent, - ':name' => $category, - ':long_name' => $data['long_name'] ?? $category, - ':project' => $data['project'] ?? '', - ':list_email' => $data['list_email'] ?? '', - ':disabled' => $data['disabled'] ?? 0, - ]); - - $catIds[$category] = $this->dbh->lastInsertId(); - } - } - - /** - * Insert fixtures in users table. - */ - public function insertBugs() - { - $sql = "INSERT INTO bugdb ( - `package_name`, - `bug_type`, - `email`, - `reporter_name`, - `sdesc`, - `ldesc`, - `php_version`, - `php_os`, - `status`, - `ts1`, - `ts2`, - `assign`, - `passwd`, - `registered`, - `block_user_comment`, - `cve_id`, - `private`, - `visitor_ip` - ) VALUES ( - :package_name, - :bug_type, - :email, - :reporter_name, - :sdesc, - :ldesc, - :php_version, - :php_os, - :status, - :ts1, - :ts2, - :assign, - :passwd, - :registered, - :block_user_comment, - :cve_id, - :private, - INET6_ATON(:visitor_ip) - ) - "; - - $bugs = $this->getBugs(); - - foreach ($bugs as $data) { - $statement = $this->dbh->prepare($sql); - - $statement->execute([ - ':package_name' => $data['package_name'], - ':bug_type' => $data['bug_type'], - ':email' => $data['email'], - ':reporter_name' => $data['reporter_name'], - ':sdesc' => $data['sdesc'], - ':ldesc' => $data['ldesc'], - ':php_version' => $data['php_version'], - ':php_os' => $data['php_os'], - ':status' => $data['status'], - ':ts1' => date('Y-m-d H:i:s'), - ':ts2' => null, - ':assign' => $data['assign'], - ':passwd' => $data['passwd'], - ':registered' => $data['registered'], - ':block_user_comment' => $data['block_user_comment'], - ':cve_id' => $data['cve_id'], - ':private' => $data['private'], - ':visitor_ip' => $data['visitor_ip'], - ]); - } - } - - /** - * Insert data in the bugs reasons table. - */ - public function insertReasons() - { - $sql = "INSERT INTO bugdb_resolves ( - `name`, - `status`, - `title`, - `message`, - `project`, - `package_name`, - `webonly` - ) VALUES ( - :name, - :status, - :title, - :message, - :project, - :package_name, - :webonly - ) - "; - - foreach ($this->getReasons() as $data) { - $statement = $this->dbh->prepare($sql); - - $statement->execute([ - ':name' => $data['name'], - ':status' => $data['status'], - ':title' => $data['title'], - ':message' => $data['message'], - ':project' => $data['project'], - ':package_name' => $data['package_name'], - ':webonly' => $data['webonly'], - ]); - } - } - - /** - * Get generated demo bug reports data with each bug having password set to - * "password". - */ - private function getBugs() - { - $bugs = []; - - // Demo secret password is always password for all bug reports - $password = bugs_get_hash('password'); - - $categories = $this->dbh->query("SELECT id, name FROM bugdb_pseudo_packages")->fetchAll(\PDO::FETCH_KEY_PAIR); - - // More random bug reports - for ($i = 0; $i < self::BUGS_COUNT; $i++) { - $username = $this->faker->unique()->userName; - $username = substr($username, 0, 16); - $username = str_replace('.', '', $username); - - $bugs[] = [ - 'package_name' => $categories[array_rand($categories, 1)], - 'bug_type' => '', - 'email' => $username.'@example.com', - 'reporter_name' => $this->faker->firstName.' '.$this->faker->lastName, - 'sdesc' => $this->faker->text(80), - 'ldesc' => $this->faker->paragraph, - 'php_version' => '7.'.rand(0, 4).'.'.rand(0, 30), - 'php_os' => $this->faker->text(32), - 'status' => array_rand($this->bugStatuses), - 'ts1' => '', - 'ts2' => '', - 'assign' => '', - 'passwd' => $password, - 'registered' => 0, - 'block_user_comment' => 'N', - 'cve_id' => '', - 'private' => 'N', - 'visitor_ip' => $this->faker->ipv4, - ]; - } - - return $bugs; - } - - /** - * Categories. - */ - private function getCategories() - { - return [ - 'General Issues' => [], - 'PDO related' => [], - 'Compile Issues' => [], - 'Configuration Issues' => [], - 'Web Server problem' => [], - 'Calendar problems' => [], - 'Compression related' => [], - 'Directory/Filesystem functions' => [], - 'Database Functions' => [], - 'Data Exchange functions' => [], - 'Extensibility Functions' => [], - 'Graphics related' => [], - 'Languages/Translation' => [], - 'Mail related' => [], - 'Encryption and hash functions' => [], - 'Network functions' => [], - 'PDF functions' => [], - 'Programming Data Structures' => [], - 'Regular expressions' => [], - 'Spelling functions' => [], - 'XML functions' => [], - 'Unicode Issues' => [], - 'Unknown/Other functions' => [], - 'PECL' => [], - 'phpdbg' => [], - 'PHP Language Specification' => [], - - // General Issues - 'Doc Build (PhD) problem' => [ - 'parent' => 'General Issues', - ], - 'Documentation problem' => [ - 'parent' => 'General Issues', - ], - 'Documentation translation problem' => [ - 'parent' => 'General Issues', - ], - 'Filter relate' => [ - 'parent' => 'General Issues', - ], - 'Online Documentation Editor problem' => [ - 'parent' => 'General Issues', - ], - 'Opcache' => [ - 'parent' => 'General Issues', - ], - 'Output Control' => [ - 'parent' => 'General Issues', - ], - 'Performance problem' => [ - 'parent' => 'General Issues', - ], - 'PHAR related' => [ - 'parent' => 'General Issues', - ], - 'PHP-GTK related' => [ - 'parent' => 'General Issues', - ], - 'Systems problem' => [ - 'parent' => 'General Issues', - ], - 'Website problem' => [ - 'parent' => 'General Issues', - ], - 'Reflection related' => [ - 'parent' => 'General Issues', - ], - 'Reproducible crash' => [ - 'parent' => 'General Issues', - ], - 'Scripting Engine problem' => [ - 'parent' => 'General Issues', - ], - 'Session related' => [ - 'parent' => 'General Issues', - ], - 'SPL related' => [ - 'parent' => 'General Issues', - ], - 'Streams related' => [ - 'parent' => 'General Issues', - ], - 'Testing related' => [ - 'parent' => 'General Issues', - ], - - // PDO related - 'PDO Core' => [ - 'parent' => 'PDO related', - ], - 'PDO DBlib' => [ - 'parent' => 'PDO related', - ], - 'PDO Firebird' => [ - 'parent' => 'PDO related', - ], - 'PDO MySQL' => [ - 'parent' => 'PDO related', - ], - 'PDO OCI' => [ - 'parent' => 'PDO related', - ], - 'PDO ODBC' => [ - 'parent' => 'PDO related', - ], - 'PDO PgSQL' => [ - 'parent' => 'PDO related', - ], - 'PDO SQLite' => [ - 'parent' => 'PDO related', - ], - - // Compile Issues - 'Compile Failure' => [ - 'parent' => 'Compile Issues', - ], - 'Compile Warning' => [ - 'parent' => 'Compile Issues', - ], - - // Configuration Issues - 'Dynamic loading' => [ - 'parent' => 'Configuration Issues', - ], - 'PHP options/info functions' => [ - 'parent' => 'Configuration Issues', - ], - 'Safe Mode/open_basedir related' => [ - 'parent' => 'Configuration Issues', - ], - 'Windows Installer related' => [ - 'parent' => 'Configuration Issues', - ], - - // TODO add more - ]; - } - - private function getReasons(): array - { - return [ - [ - 'name' => 'trysnapshot54', - 'status' => 'Feedback', - 'title' => 'Try a snapshot (PHP 5.4)', - 'message' => 'Please try using this snapshot: - - http://snaps.php.net/php5.4-latest.tar.gz - - For Windows: - - http://windows.php.net/snapshots/', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'trysnapshot55', - 'status' => 'Feedback', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'trysnapshottrunk', - 'status' => 'Feedback', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'alreadyfixed', - 'status' => 'Closed', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'needtrace', - 'status' => 'Feedback', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'needscript', - 'status' => 'Feedback', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'oldversion', - 'status' => 'Not a bug', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'support', - 'status' => 'Not a bug', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'nofeedback', - 'status' => 'No Feedback', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 1, - ], - [ - 'name' => 'notwrong', - 'status' => 'Not a bug', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'notenoughinfo', - 'status' => 'Feedback', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'submittedtwice', - 'status' => 'Not a bug', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'globals', - 'status' => 'Not a bug', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'php4', - 'status' => 'Wont fix', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'dst', - 'status' => 'Not a bug', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'isapi', - 'status' => 'Not a bug', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'gnused', - 'status' => 'Not a bug', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'float', - 'status' => 'Not a bug', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'nozend', - 'status' => 'Not a bug', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - [ - 'name' => 'mysqlcfg', - 'status' => 'Not a bug', - 'title' => '', - 'message' => '', - 'project' => 'php', - 'package_name' => '', - 'webonly' => 0, - ], - ]; - } -} diff --git a/tests/Unit/Command/InsertFixturesCommandTest.php b/tests/Unit/Command/InsertFixturesCommandTest.php deleted file mode 100644 index bbbacc6a..00000000 --- a/tests/Unit/Command/InsertFixturesCommandTest.php +++ /dev/null @@ -1,65 +0,0 @@ -dbh = new \PDO('sqlite::memory:'); - } - - public function testExecute(): void - { - $insertFixturesCommand = new InsertFixturesCommand( - $this->dbh, - new AppFixtures($this->dbh, Factory::create(), []), - 'dev' - ); - - $application = new Application(); - $application->add($insertFixturesCommand); - - $command = $application->find('app:fixtures:insert'); - $commandTester = new CommandTester($command); - $commandTester->setInputs(['n']); - $exitCode = $commandTester->execute(['command' => $command->getName()]); - - $this->assertRegExp('/.../', $commandTester->getDisplay()); - $this->assertSame(0, $exitCode); - } - - public function testExecuteInProdEnv(): void - { - $insertFixturesCommand = new InsertFixturesCommand( - $this->dbh, - new AppFixtures($this->dbh, Factory::create(), []), - 'prod' - ); - - $application = new Application(); - $application->add($insertFixturesCommand); - - $command = $application->find('app:fixtures:insert'); - $commandTester = new CommandTester($command); - $commandTester->setInputs(['n']); - $exitCode = $commandTester->execute(['command' => $command->getName()]); - - $this->assertRegExp('/This command can be executed only in dev/', $commandTester->getDisplay()); - $this->assertSame(0, $exitCode); - } -} From def044bbda998a9dce27d4ddd75af445367a9039 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Tue, 28 May 2019 13:18:28 +0100 Subject: [PATCH 203/277] stop spammy users --- include/functions.php | 8 ++++++++ www/bug.php | 14 +++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/include/functions.php b/include/functions.php index a4e3a0c9..dfca9009 100644 --- a/include/functions.php +++ b/include/functions.php @@ -244,6 +244,14 @@ function is_spam($string) return false; } +/* Primitive check for SPAMmy user. Add more later. */ +function is_spam_user($email) +{ + if (preg_match("/(rhsoft)/i", $email)) { + return true; + } + return false; +} /** * Obfuscates email addresses to hinder spammer's spiders diff --git a/www/bug.php b/www/bug.php index 76abf0cf..c49f69fc 100644 --- a/www/bug.php +++ b/www/bug.php @@ -227,10 +227,12 @@ if (is_spam($ncomment)) { $errors[] = SPAM_REJECT_MESSAGE; } - if (is_spam($_POST['in']['commentemail'])) { $errors[] = "Please do not SPAM our bug system."; } + if (is_spam_user($_POST['in']['commentemail'])) { + $errors[] = "Please do not SPAM our bug system."; + } if (!$errors) { do { @@ -267,6 +269,9 @@ } $from = $_POST['in']['commentemail']; + if (is_spam_user($from)) { + $errors[] = "Please do not SPAM our bug system."; + } } elseif (isset($_POST['in']) && !isset($_POST['preview']) && $edit == 2) { // Edits submitted by original reporter for old bugs @@ -324,6 +329,10 @@ $from = $bug['email']; } + if (is_spam_user($from)) { + $errors[] = "Please do not SPAM our bug system."; + } + if (!$errors && !($errors = incoming_details_are_valid($_POST['in'], false))) { // Allow the reporter to change the bug type to 'Security', hence mark // the report as private @@ -382,6 +391,9 @@ if (is_spam($ncomment)) { $errors[] = SPAM_REJECT_MESSAGE; } + if (is_spam_user($from)) { + $errors[] = "Please do not SPAM our bug system."; + } } elseif (isset($_POST['in']) && is_array($_POST['in']) && !isset($_POST['preview']) && $edit == 1) { // Edits submitted by developer From f2df1a664b3642f9dc4fea449c8d74c565670081 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Sat, 1 Jun 2019 00:59:55 +0200 Subject: [PATCH 204/277] Two can play that game --- include/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/functions.php b/include/functions.php index dfca9009..d261294c 100644 --- a/include/functions.php +++ b/include/functions.php @@ -247,7 +247,7 @@ function is_spam($string) /* Primitive check for SPAMmy user. Add more later. */ function is_spam_user($email) { - if (preg_match("/(rhsoft)/i", $email)) { + if (preg_match("/(rhsoft|reindl)/i", $email)) { return true; } return false; From 9f5c30ffabf31ed9216fe34535eb4de5e35d907e Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Fri, 14 Jun 2019 12:03:34 +0200 Subject: [PATCH 205/277] Add search link for PHP 7.4 bugs --- www/index.php | 1 + 1 file changed, 1 insertion(+) diff --git a/www/index.php b/www/index.php index c57a9ace..ce3e68cf 100644 --- a/www/index.php +++ b/www/index.php @@ -29,6 +29,7 @@ 'Most recent open bugs (PHP 7.1)' => '&bug_type=All&phpver=7.1', 'Most recent open bugs (PHP 7.2)' => '&bug_type=All&phpver=7.2', 'Most recent open bugs (PHP 7.3)' => '&bug_type=All&phpver=7.3', + 'Most recent open bugs (PHP 7.4)' => '&bug_type=All&phpver=7.4', 'Open Documentation bugs' => '&bug_type=Documentation+Problem', 'Open Documentation bugs (with patches)' => '&bug_type=Documentation+Problem&patch=Y', ]; From 4b52935a8675d6ff6d5b06258e6c74ef135fad35 Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Wed, 17 Jul 2019 22:52:23 -0400 Subject: [PATCH 206/277] Get reasons from hard-coded list, instead of database This commit changes the source in a way that looks identical to the original format. Follow diffs should consider reorganizing the data structure to remove DB specific fields like ID, and remove the Repository container pattern. --- src/Repository/ReasonRepository.php | 427 ++++++++++++++++++++++++++-- 1 file changed, 402 insertions(+), 25 deletions(-) diff --git a/src/Repository/ReasonRepository.php b/src/Repository/ReasonRepository.php index b33d60a6..f46fdd7c 100644 --- a/src/Repository/ReasonRepository.php +++ b/src/Repository/ReasonRepository.php @@ -3,22 +3,410 @@ namespace App\Repository; /** - * Repository class for fetching data from the bugdb_resolves database table. + * Repository class for returning "quick fix" reasons + * as if they had come from a database. + * + * As of July 2019, these are no longer stored in bugsdb, + * but are included as a hard-coded list. */ class ReasonRepository { - /** - * Database handle. - * @var \PDO - */ - private $dbh; + const REASONS = [ + [ + 'id' => '1', + 'name' => 'trysnapshot54', + 'status' => 'Feedback', + 'title' => 'Try a snapshot (PHP 5.4)', + 'message' => 'Please try using this snapshot: + + http://snaps.php.net/php5.4-latest.tar.gz + +For Windows: + + http://windows.php.net/snapshots/', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '2', + 'name' => 'trysnapshot55', + 'status' => 'Feedback', + 'title' => 'Try a snapshot (PHP 5.5)', + 'message' => 'Please try using this snapshot: + + http://snaps.php.net/php5.5-latest.tar.gz + +For Windows: + + http://windows.php.net/snapshots/', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '3', + 'name' => 'trysnapshottrunk', + 'status' => 'Feedback', + 'title' => 'Try a snapshot (trunk)', + 'message' => 'Please try using this snapshot: + + http://snaps.php.net/php-trunk-latest.tar.gz + +For Windows: + + http://windows.php.net/snapshots/', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '4', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'The fix for this bug has been committed. + +Snapshots of the sources are packaged every three hours; this change +will be in the next snapshot. You can grab the snapshot at +http://snaps.php.net/. + + For Windows: + +http://windows.php.net/snapshots/ + +Thank you for the report, and for helping us make PHP better.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '5', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'The fix for this bug has been committed. Since the websites are not directly +updated from the repository, the fix might need some time to spread +across the globe to all mirror sites, including PHP.net itself. + +Thank you for the report, and for helping us make PHP.net better.', + 'project' => 'php', + 'package_name' => 'Website problem', + 'webonly' => '0', + ], [ + 'id' => '6', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'This bug has been fixed in the documentation\'s XML sources. Since the +online and downloadable versions of the documentation need some time +to get updated, we would like to ask you to be a bit patient. + +Thank you for the report, and for helping us make our documentation better.', + 'project' => 'php', + 'package_name' => 'Documentation problem', + 'webonly' => '0', + ], [ + 'id' => '7', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'This bug has been fixed in the documentation\'s XML sources. Since the +online and downloadable versions of the documentation need some time +to get updated, we would like to ask you to be a bit patient. + +Thank you for the report, and for helping us make our documentation better.', + 'project' => 'php', + 'package_name' => 'Translation problem', + 'webonly' => '0', + ], [ + 'id' => '8', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'The fix for this bug has been committed. Since the websites are not directly +updated from the repository, the fix might need some time to spread +across the globe to all mirror sites, including PHP.net itself. + +Thank you for the report, and for helping us make PHP.net better.', + 'project' => 'php', + 'package_name' => 'Doc Build problem', + 'webonly' => '0', + ], [ + 'id' => '9', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'This bug has been fixed in SVN, and should show up online in an +hour or three. + +Thank you for the report, and for helping us make PHP.net better.', + 'project' => 'php', + 'package_name' => 'Online Doc Editor problem', + 'webonly' => '0', + ], [ + 'id' => '10', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'The fix for this bug has been committed. Since the PHP Documentation Tools website +is updated from the repository at a regular interval, the fix might need +some time to take effect. + +Thank you for the report, and for helping us make DocWeb better.', + 'project' => 'php', + 'package_name' => 'DocWeb problem', + 'webonly' => '0', + ], [ + 'id' => '12', + 'name' => 'alreadyfixed', + 'status' => 'Closed', + 'title' => 'Fixed in release', + 'message' => 'Thank you for your bug report. This issue has already been fixed +in the latest released version of PHP, which you can download at +http://www.php.net/downloads.php', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '13', + 'name' => 'needtrace', + 'status' => 'Feedback', + 'title' => 'Need backtrace', + 'message' => 'Thank you for this bug report. To properly diagnose the problem, we +need a backtrace to see what is happening behind the scenes. To +find out how to generate a backtrace, please read +http://bugs.php.net/bugs-generating-backtrace.php for *NIX and +http://bugs.php.net/bugs-generating-backtrace-win32.php for Win32 + +Once you have generated a backtrace, please submit it to this bug +report and change the status back to "Open". Thank you for helping +us make PHP better.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '14', + 'name' => 'needscript', + 'status' => 'Feedback', + 'title' => 'Need Reproduce Script', + 'message' => 'Thank you for this bug report. To properly diagnose the problem, we +need a short but complete example script to be able to reproduce +this bug ourselves. + +A proper reproducing script starts with , +is max. 10-20 lines long and does not require any external +resources such as databases, etc. If the script requires a +database to demonstrate the issue, please make sure it creates +all necessary tables, stored procedures etc. + +Please avoid embedding huge scripts into the report.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '15', + 'name' => 'oldversion', + 'status' => 'Not a bug', + 'title' => 'Try newer version', + 'message' => 'Thank you for taking the time to report a problem with PHP. +Unfortunately you are not using a current version of PHP -- +the problem might already be fixed. Please download a new +PHP version from http://www.php.net/downloads.php + +If you are able to reproduce the bug with one of the latest +versions of PHP, please change the PHP version on this bug report +to the version you tested and change the status back to "Open". +Again, thank you for your continued support of PHP.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '16', + 'name' => 'support', + 'status' => 'Not a bug', + 'title' => 'Not developer issue', + 'message' => 'Sorry, but your problem does not imply a bug in PHP itself. For a +list of more appropriate places to ask for help using PHP, please +visit http://www.php.net/support.php as this bug system is not the +appropriate forum for asking support questions. Due to the volume +of reports we can not explain in detail here why your report is not +a bug. The support channels will be able to provide an explanation +for you. + +Thank you for your interest in PHP.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '17', + 'name' => 'nofeedback', + 'status' => 'No Feedback', + 'title' => 'No feedback', + 'message' => 'No feedback was provided. The bug is being suspended because +we assume that you are no longer experiencing the problem. +If this is not the case and you are able to provide the +information that was requested earlier, please do so and +change the status of the bug back to "Re-Opened". Thank you.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '1', + ], [ + 'id' => '18', + 'name' => 'notwrong', + 'status' => 'Not a bug', + 'title' => 'Expected behavior', + 'message' => 'Thank you for taking the time to write to us, but this is not +a bug. Please double-check the documentation available at +http://www.php.net/manual/ and the instructions on how to report +a bug at http://bugs.php.net/how-to-report.php', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '19', + 'name' => 'notenoughinfo', + 'status' => 'Feedback', + 'title' => 'Not enough info', + 'message' => 'Not enough information was provided for us to be able +to handle this bug. Please re-read the instructions at +http://bugs.php.net/how-to-report.php + +If you can provide more information, feel free to add it +to this bug and change the status back to "Open". + +Thank you for your interest in PHP.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '20', + 'name' => 'submittedtwice', + 'status' => 'Not a bug', + 'title' => 'Submitted twice', + 'message' => 'Please do not submit the same bug more than once. An existing +bug report already describes this very problem. Even if you feel +that your issue is somewhat different, the resolution is likely +to be the same. + +Thank you for your interest in PHP.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '21', + 'name' => 'globals', + 'status' => 'Not a bug', + 'title' => 'register_globals', + 'message' => 'In PHP 4.2.0, the \'register_globals\' setting default changed to +\'off\'. See http://www.php.net/release_4_2_0.php for more info. +We are sorry about the inconvenience, but this change was a necessary +part of our efforts to make PHP scripting more secure and portable.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '22', + 'name' => 'php4', + 'status' => 'Wont fix', + 'title' => 'PHP 4 support discontinued', + 'message' => 'We are sorry, but we can not support PHP 4 related problems anymore.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '23', + 'name' => 'dst', + 'status' => 'Not a bug', + 'title' => 'Daylight Savings', + 'message' => 'We are happy to tell you that you just discovered Daylight Savings +Time. For more information see: +http://webexhibits.org/daylightsaving/b.html +Instead of using mktime/date consider using gmmktime and gmdate which do +not suffer from DST.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '24', + 'name' => 'isapi', + 'status' => 'Not a bug', + 'title' => 'IIS Stability', + 'message' => 'We are aware of PHP\'s problems with stability under IIS and are working +to rectify the problem. Unfortunatly your bug report does not contain any +extra useful information and we already have enough bug reports open about +this issue. If you can provide more detailed information such as a +reproducable crash or a backtrace please do so and reopen this bug. +Otherwise please keep trying new releases as we are working to resolve +the problems on this platform + +Thanks for your interest in PHP.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '25', + 'name' => 'gnused', + 'status' => 'Not a bug', + 'title' => 'Install GNU Sed', + 'message' => 'Due to a bug in the installed sed on your system the build +fails. Install GNU sed and it should be okay. + +Thank you for your interest in PHP.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '26', + 'name' => 'float', + 'status' => 'Not a bug', + 'title' => 'Floating point limitations', + 'message' => 'Floating point values have a limited precision. Hence a value might +not have the same string representation after any processing. That also +includes writing a floating point value in your script and directly +printing it without any mathematical operations. + +If you would like to know more about "floats" and what IEEE +754 is, read this: +http://www.floating-point-gui.de/ + +Thank you for your interest in PHP.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '27', + 'name' => 'nozend', + 'status' => 'Not a bug', + 'title' => 'No Zend Extensions', + 'message' => 'Do not file bugs when you have Zend extensions (zend_extension=) +loaded. Examples are Zend Optimizer, Zend Debugger, Turck MM Cache, +APC, Xdebug and ionCube loader. These extensions often modify engine +behavior which is not related to PHP itself.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '28', + 'name' => 'mysqlcfg', + 'status' => 'Not a bug', + 'title' => 'MySQL Configuration Error', + 'message' => 'When using the mysqli extension together with the mysql extension +you have to use the same libraries and include files. mysqli +extension requires the location of mysql_config file, mysql +extension requires the path of your mysql installation. + +If you installed MySQL 4.1 for example with prefix /usr/local/mysql-4.1 +your configure settings should be +--with-mysql=/usr/local/mysql-4.1 +--with-mysqli=/usr/local/mysql-4.1/bin/mysql_config', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], + ]; /** * Class constructor */ public function __construct(\PDO $dbh) { - $this->dbh = $dbh; } /** @@ -26,23 +414,14 @@ public function __construct(\PDO $dbh) */ public function findByProject(string $project = ''): array { - $sql = 'SELECT * FROM bugdb_resolves'; - $arguments = []; - - if ($project !== '') { - $sql .= " WHERE (project = ? OR project = '')"; - $arguments[] = $project; - } + $reasons = array_filter( + self::REASONS, + function ($reason) use ($project) { + return ($reason['project'] ?? '') === $project; + }); $resolves = $variations = []; - $statement = $this->dbh->prepare($sql); - $exec = $statement->execute($arguments); - - if (!$exec) { - throw new \Exception('Error when fetching resolve reasons.'); - } - - while ($row = $statement->fetch()) { + foreach ($reasons as $row) { if (!empty($row['package_name'])) { $variations[$row['name']][$row['package_name']] = $row['message']; } else { @@ -58,8 +437,6 @@ public function findByProject(string $project = ''): array */ public function findAll(): array { - $sql = 'SELECT * FROM bugdb_resolves'; - - return $this->dbh->query($sql)->fetchAll(); + return self::REASONS; } } From 87120c33ebb565e547bd010c3f31f06529b846fd Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Thu, 18 Jul 2019 10:34:26 -0400 Subject: [PATCH 207/277] Fix filtering logic --- src/Repository/ReasonRepository.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Repository/ReasonRepository.php b/src/Repository/ReasonRepository.php index f46fdd7c..9e4265a1 100644 --- a/src/Repository/ReasonRepository.php +++ b/src/Repository/ReasonRepository.php @@ -414,11 +414,15 @@ public function __construct(\PDO $dbh) */ public function findByProject(string $project = ''): array { - $reasons = array_filter( - self::REASONS, - function ($reason) use ($project) { - return ($reason['project'] ?? '') === $project; - }); + $reasons = self::REASONS; + if ($project !== '') { + $reasons = array_filter( + $reasons, + function ($reason) use ($project) { + return ((($reason['project'] ?? '') === $project) || + (($reason['project'] ?? '') === '')); + }); + } $resolves = $variations = []; foreach ($reasons as $row) { From e7ef0c62cec5409a997e80038d8ee37119062e5c Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Thu, 18 Jul 2019 10:35:12 -0400 Subject: [PATCH 208/277] Flip whitespace to match the original file's 4-space format --- src/Repository/ReasonRepository.php | 526 ++++++++++++++-------------- 1 file changed, 263 insertions(+), 263 deletions(-) diff --git a/src/Repository/ReasonRepository.php b/src/Repository/ReasonRepository.php index 9e4265a1..a9c8ca06 100644 --- a/src/Repository/ReasonRepository.php +++ b/src/Repository/ReasonRepository.php @@ -11,58 +11,58 @@ */ class ReasonRepository { - const REASONS = [ - [ - 'id' => '1', - 'name' => 'trysnapshot54', - 'status' => 'Feedback', - 'title' => 'Try a snapshot (PHP 5.4)', - 'message' => 'Please try using this snapshot: + const REASONS = [ + [ + 'id' => '1', + 'name' => 'trysnapshot54', + 'status' => 'Feedback', + 'title' => 'Try a snapshot (PHP 5.4)', + 'message' => 'Please try using this snapshot: - http://snaps.php.net/php5.4-latest.tar.gz + http://snaps.php.net/php5.4-latest.tar.gz For Windows: - http://windows.php.net/snapshots/', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '2', - 'name' => 'trysnapshot55', - 'status' => 'Feedback', - 'title' => 'Try a snapshot (PHP 5.5)', - 'message' => 'Please try using this snapshot: + http://windows.php.net/snapshots/', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '2', + 'name' => 'trysnapshot55', + 'status' => 'Feedback', + 'title' => 'Try a snapshot (PHP 5.5)', + 'message' => 'Please try using this snapshot: - http://snaps.php.net/php5.5-latest.tar.gz + http://snaps.php.net/php5.5-latest.tar.gz For Windows: - http://windows.php.net/snapshots/', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '3', - 'name' => 'trysnapshottrunk', - 'status' => 'Feedback', - 'title' => 'Try a snapshot (trunk)', - 'message' => 'Please try using this snapshot: + http://windows.php.net/snapshots/', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '3', + 'name' => 'trysnapshottrunk', + 'status' => 'Feedback', + 'title' => 'Try a snapshot (trunk)', + 'message' => 'Please try using this snapshot: - http://snaps.php.net/php-trunk-latest.tar.gz + http://snaps.php.net/php-trunk-latest.tar.gz For Windows: - http://windows.php.net/snapshots/', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '4', - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => 'Fixed in SVN', - 'message' => 'The fix for this bug has been committed. + http://windows.php.net/snapshots/', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '4', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'The fix for this bug has been committed. Snapshots of the sources are packaged every three hours; this change will be in the next snapshot. You can grab the snapshot at @@ -73,103 +73,103 @@ class ReasonRepository http://windows.php.net/snapshots/ Thank you for the report, and for helping us make PHP better.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '5', - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => 'Fixed in SVN', - 'message' => 'The fix for this bug has been committed. Since the websites are not directly + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '5', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'The fix for this bug has been committed. Since the websites are not directly updated from the repository, the fix might need some time to spread across the globe to all mirror sites, including PHP.net itself. Thank you for the report, and for helping us make PHP.net better.', - 'project' => 'php', - 'package_name' => 'Website problem', - 'webonly' => '0', - ], [ - 'id' => '6', - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => 'Fixed in SVN', - 'message' => 'This bug has been fixed in the documentation\'s XML sources. Since the + 'project' => 'php', + 'package_name' => 'Website problem', + 'webonly' => '0', + ], [ + 'id' => '6', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'This bug has been fixed in the documentation\'s XML sources. Since the online and downloadable versions of the documentation need some time to get updated, we would like to ask you to be a bit patient. Thank you for the report, and for helping us make our documentation better.', - 'project' => 'php', - 'package_name' => 'Documentation problem', - 'webonly' => '0', - ], [ - 'id' => '7', - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => 'Fixed in SVN', - 'message' => 'This bug has been fixed in the documentation\'s XML sources. Since the + 'project' => 'php', + 'package_name' => 'Documentation problem', + 'webonly' => '0', + ], [ + 'id' => '7', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'This bug has been fixed in the documentation\'s XML sources. Since the online and downloadable versions of the documentation need some time to get updated, we would like to ask you to be a bit patient. Thank you for the report, and for helping us make our documentation better.', - 'project' => 'php', - 'package_name' => 'Translation problem', - 'webonly' => '0', - ], [ - 'id' => '8', - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => 'Fixed in SVN', - 'message' => 'The fix for this bug has been committed. Since the websites are not directly + 'project' => 'php', + 'package_name' => 'Translation problem', + 'webonly' => '0', + ], [ + 'id' => '8', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'The fix for this bug has been committed. Since the websites are not directly updated from the repository, the fix might need some time to spread across the globe to all mirror sites, including PHP.net itself. Thank you for the report, and for helping us make PHP.net better.', - 'project' => 'php', - 'package_name' => 'Doc Build problem', - 'webonly' => '0', - ], [ - 'id' => '9', - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => 'Fixed in SVN', - 'message' => 'This bug has been fixed in SVN, and should show up online in an + 'project' => 'php', + 'package_name' => 'Doc Build problem', + 'webonly' => '0', + ], [ + 'id' => '9', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'This bug has been fixed in SVN, and should show up online in an hour or three. Thank you for the report, and for helping us make PHP.net better.', - 'project' => 'php', - 'package_name' => 'Online Doc Editor problem', - 'webonly' => '0', - ], [ - 'id' => '10', - 'name' => 'fixed', - 'status' => 'Closed', - 'title' => 'Fixed in SVN', - 'message' => 'The fix for this bug has been committed. Since the PHP Documentation Tools website + 'project' => 'php', + 'package_name' => 'Online Doc Editor problem', + 'webonly' => '0', + ], [ + 'id' => '10', + 'name' => 'fixed', + 'status' => 'Closed', + 'title' => 'Fixed in SVN', + 'message' => 'The fix for this bug has been committed. Since the PHP Documentation Tools website is updated from the repository at a regular interval, the fix might need some time to take effect. Thank you for the report, and for helping us make DocWeb better.', - 'project' => 'php', - 'package_name' => 'DocWeb problem', - 'webonly' => '0', - ], [ - 'id' => '12', - 'name' => 'alreadyfixed', - 'status' => 'Closed', - 'title' => 'Fixed in release', - 'message' => 'Thank you for your bug report. This issue has already been fixed + 'project' => 'php', + 'package_name' => 'DocWeb problem', + 'webonly' => '0', + ], [ + 'id' => '12', + 'name' => 'alreadyfixed', + 'status' => 'Closed', + 'title' => 'Fixed in release', + 'message' => 'Thank you for your bug report. This issue has already been fixed in the latest released version of PHP, which you can download at http://www.php.net/downloads.php', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '13', - 'name' => 'needtrace', - 'status' => 'Feedback', - 'title' => 'Need backtrace', - 'message' => 'Thank you for this bug report. To properly diagnose the problem, we + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '13', + 'name' => 'needtrace', + 'status' => 'Feedback', + 'title' => 'Need backtrace', + 'message' => 'Thank you for this bug report. To properly diagnose the problem, we need a backtrace to see what is happening behind the scenes. To find out how to generate a backtrace, please read http://bugs.php.net/bugs-generating-backtrace.php for *NIX and @@ -178,15 +178,15 @@ class ReasonRepository Once you have generated a backtrace, please submit it to this bug report and change the status back to "Open". Thank you for helping us make PHP better.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '14', - 'name' => 'needscript', - 'status' => 'Feedback', - 'title' => 'Need Reproduce Script', - 'message' => 'Thank you for this bug report. To properly diagnose the problem, we + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '14', + 'name' => 'needscript', + 'status' => 'Feedback', + 'title' => 'Need Reproduce Script', + 'message' => 'Thank you for this bug report. To properly diagnose the problem, we need a short but complete example script to be able to reproduce this bug ourselves. @@ -197,15 +197,15 @@ class ReasonRepository all necessary tables, stored procedures etc. Please avoid embedding huge scripts into the report.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '15', - 'name' => 'oldversion', - 'status' => 'Not a bug', - 'title' => 'Try newer version', - 'message' => 'Thank you for taking the time to report a problem with PHP. + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '15', + 'name' => 'oldversion', + 'status' => 'Not a bug', + 'title' => 'Try newer version', + 'message' => 'Thank you for taking the time to report a problem with PHP. Unfortunately you are not using a current version of PHP -- the problem might already be fixed. Please download a new PHP version from http://www.php.net/downloads.php @@ -214,15 +214,15 @@ class ReasonRepository versions of PHP, please change the PHP version on this bug report to the version you tested and change the status back to "Open". Again, thank you for your continued support of PHP.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '16', - 'name' => 'support', - 'status' => 'Not a bug', - 'title' => 'Not developer issue', - 'message' => 'Sorry, but your problem does not imply a bug in PHP itself. For a + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '16', + 'name' => 'support', + 'status' => 'Not a bug', + 'title' => 'Not developer issue', + 'message' => 'Sorry, but your problem does not imply a bug in PHP itself. For a list of more appropriate places to ask for help using PHP, please visit http://www.php.net/support.php as this bug system is not the appropriate forum for asking support questions. Due to the volume @@ -231,40 +231,40 @@ class ReasonRepository for you. Thank you for your interest in PHP.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '17', - 'name' => 'nofeedback', - 'status' => 'No Feedback', - 'title' => 'No feedback', - 'message' => 'No feedback was provided. The bug is being suspended because + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '17', + 'name' => 'nofeedback', + 'status' => 'No Feedback', + 'title' => 'No feedback', + 'message' => 'No feedback was provided. The bug is being suspended because we assume that you are no longer experiencing the problem. If this is not the case and you are able to provide the information that was requested earlier, please do so and change the status of the bug back to "Re-Opened". Thank you.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '1', - ], [ - 'id' => '18', - 'name' => 'notwrong', - 'status' => 'Not a bug', - 'title' => 'Expected behavior', - 'message' => 'Thank you for taking the time to write to us, but this is not + 'project' => 'php', + 'package_name' => '', + 'webonly' => '1', + ], [ + 'id' => '18', + 'name' => 'notwrong', + 'status' => 'Not a bug', + 'title' => 'Expected behavior', + 'message' => 'Thank you for taking the time to write to us, but this is not a bug. Please double-check the documentation available at http://www.php.net/manual/ and the instructions on how to report a bug at http://bugs.php.net/how-to-report.php', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '19', - 'name' => 'notenoughinfo', - 'status' => 'Feedback', - 'title' => 'Not enough info', - 'message' => 'Not enough information was provided for us to be able + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '19', + 'name' => 'notenoughinfo', + 'status' => 'Feedback', + 'title' => 'Not enough info', + 'message' => 'Not enough information was provided for us to be able to handle this bug. Please re-read the instructions at http://bugs.php.net/how-to-report.php @@ -272,63 +272,63 @@ class ReasonRepository to this bug and change the status back to "Open". Thank you for your interest in PHP.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '20', - 'name' => 'submittedtwice', - 'status' => 'Not a bug', - 'title' => 'Submitted twice', - 'message' => 'Please do not submit the same bug more than once. An existing + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '20', + 'name' => 'submittedtwice', + 'status' => 'Not a bug', + 'title' => 'Submitted twice', + 'message' => 'Please do not submit the same bug more than once. An existing bug report already describes this very problem. Even if you feel that your issue is somewhat different, the resolution is likely to be the same. Thank you for your interest in PHP.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '21', - 'name' => 'globals', - 'status' => 'Not a bug', - 'title' => 'register_globals', - 'message' => 'In PHP 4.2.0, the \'register_globals\' setting default changed to + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '21', + 'name' => 'globals', + 'status' => 'Not a bug', + 'title' => 'register_globals', + 'message' => 'In PHP 4.2.0, the \'register_globals\' setting default changed to \'off\'. See http://www.php.net/release_4_2_0.php for more info. We are sorry about the inconvenience, but this change was a necessary part of our efforts to make PHP scripting more secure and portable.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '22', - 'name' => 'php4', - 'status' => 'Wont fix', - 'title' => 'PHP 4 support discontinued', - 'message' => 'We are sorry, but we can not support PHP 4 related problems anymore.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '23', - 'name' => 'dst', - 'status' => 'Not a bug', - 'title' => 'Daylight Savings', - 'message' => 'We are happy to tell you that you just discovered Daylight Savings + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '22', + 'name' => 'php4', + 'status' => 'Wont fix', + 'title' => 'PHP 4 support discontinued', + 'message' => 'We are sorry, but we can not support PHP 4 related problems anymore.', + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '23', + 'name' => 'dst', + 'status' => 'Not a bug', + 'title' => 'Daylight Savings', + 'message' => 'We are happy to tell you that you just discovered Daylight Savings Time. For more information see: http://webexhibits.org/daylightsaving/b.html Instead of using mktime/date consider using gmmktime and gmdate which do not suffer from DST.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '24', - 'name' => 'isapi', - 'status' => 'Not a bug', - 'title' => 'IIS Stability', - 'message' => 'We are aware of PHP\'s problems with stability under IIS and are working + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '24', + 'name' => 'isapi', + 'status' => 'Not a bug', + 'title' => 'IIS Stability', + 'message' => 'We are aware of PHP\'s problems with stability under IIS and are working to rectify the problem. Unfortunatly your bug report does not contain any extra useful information and we already have enough bug reports open about this issue. If you can provide more detailed information such as a @@ -337,27 +337,27 @@ class ReasonRepository the problems on this platform Thanks for your interest in PHP.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '25', - 'name' => 'gnused', - 'status' => 'Not a bug', - 'title' => 'Install GNU Sed', - 'message' => 'Due to a bug in the installed sed on your system the build + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '25', + 'name' => 'gnused', + 'status' => 'Not a bug', + 'title' => 'Install GNU Sed', + 'message' => 'Due to a bug in the installed sed on your system the build fails. Install GNU sed and it should be okay. Thank you for your interest in PHP.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '26', - 'name' => 'float', - 'status' => 'Not a bug', - 'title' => 'Floating point limitations', - 'message' => 'Floating point values have a limited precision. Hence a value might + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '26', + 'name' => 'float', + 'status' => 'Not a bug', + 'title' => 'Floating point limitations', + 'message' => 'Floating point values have a limited precision. Hence a value might not have the same string representation after any processing. That also includes writing a floating point value in your script and directly printing it without any mathematical operations. @@ -367,27 +367,27 @@ class ReasonRepository http://www.floating-point-gui.de/ Thank you for your interest in PHP.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '27', - 'name' => 'nozend', - 'status' => 'Not a bug', - 'title' => 'No Zend Extensions', - 'message' => 'Do not file bugs when you have Zend extensions (zend_extension=) + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '27', + 'name' => 'nozend', + 'status' => 'Not a bug', + 'title' => 'No Zend Extensions', + 'message' => 'Do not file bugs when you have Zend extensions (zend_extension=) loaded. Examples are Zend Optimizer, Zend Debugger, Turck MM Cache, APC, Xdebug and ionCube loader. These extensions often modify engine behavior which is not related to PHP itself.', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '28', - 'name' => 'mysqlcfg', - 'status' => 'Not a bug', - 'title' => 'MySQL Configuration Error', - 'message' => 'When using the mysqli extension together with the mysql extension + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], [ + 'id' => '28', + 'name' => 'mysqlcfg', + 'status' => 'Not a bug', + 'title' => 'MySQL Configuration Error', + 'message' => 'When using the mysqli extension together with the mysql extension you have to use the same libraries and include files. mysqli extension requires the location of mysql_config file, mysql extension requires the path of your mysql installation. @@ -396,11 +396,11 @@ class ReasonRepository your configure settings should be --with-mysql=/usr/local/mysql-4.1 --with-mysqli=/usr/local/mysql-4.1/bin/mysql_config', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], - ]; + 'project' => 'php', + 'package_name' => '', + 'webonly' => '0', + ], + ]; /** * Class constructor @@ -414,18 +414,18 @@ public function __construct(\PDO $dbh) */ public function findByProject(string $project = ''): array { - $reasons = self::REASONS; - if ($project !== '') { - $reasons = array_filter( - $reasons, - function ($reason) use ($project) { - return ((($reason['project'] ?? '') === $project) || - (($reason['project'] ?? '') === '')); - }); - } + $reasons = self::REASONS; + if ($project !== '') { + $reasons = array_filter( + $reasons, + function ($reason) use ($project) { + return ((($reason['project'] ?? '') === $project) || + (($reason['project'] ?? '') === '')); + }); + } $resolves = $variations = []; - foreach ($reasons as $row) { + foreach ($reasons as $row) { if (!empty($row['package_name'])) { $variations[$row['name']][$row['package_name']] = $row['message']; } else { @@ -441,6 +441,6 @@ function ($reason) use ($project) { */ public function findAll(): array { - return self::REASONS; + return self::REASONS; } } From e8be4914ec55290cccb920dffd4ffd10bc26ddd2 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Mon, 29 Jul 2019 13:36:29 -0700 Subject: [PATCH 209/277] Update bug resolution reasons Remove SVN references, and snaps doesn't seem to work anymore, so don't refer to it. --- src/Repository/ReasonRepository.php | 86 +++++++---------------------- 1 file changed, 20 insertions(+), 66 deletions(-) diff --git a/src/Repository/ReasonRepository.php b/src/Repository/ReasonRepository.php index a9c8ca06..d5c7cf13 100644 --- a/src/Repository/ReasonRepository.php +++ b/src/Repository/ReasonRepository.php @@ -12,66 +12,13 @@ class ReasonRepository { const REASONS = [ - [ - 'id' => '1', - 'name' => 'trysnapshot54', - 'status' => 'Feedback', - 'title' => 'Try a snapshot (PHP 5.4)', - 'message' => 'Please try using this snapshot: - - http://snaps.php.net/php5.4-latest.tar.gz - -For Windows: - - http://windows.php.net/snapshots/', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '2', - 'name' => 'trysnapshot55', - 'status' => 'Feedback', - 'title' => 'Try a snapshot (PHP 5.5)', - 'message' => 'Please try using this snapshot: - - http://snaps.php.net/php5.5-latest.tar.gz - -For Windows: - - http://windows.php.net/snapshots/', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ - 'id' => '3', - 'name' => 'trysnapshottrunk', - 'status' => 'Feedback', - 'title' => 'Try a snapshot (trunk)', - 'message' => 'Please try using this snapshot: - - http://snaps.php.net/php-trunk-latest.tar.gz - -For Windows: - - http://windows.php.net/snapshots/', - 'project' => 'php', - 'package_name' => '', - 'webonly' => '0', - ], [ + [ 'id' => '4', 'name' => 'fixed', 'status' => 'Closed', - 'title' => 'Fixed in SVN', + 'title' => 'Fix committed', 'message' => 'The fix for this bug has been committed. - -Snapshots of the sources are packaged every three hours; this change -will be in the next snapshot. You can grab the snapshot at -http://snaps.php.net/. - - For Windows: - -http://windows.php.net/snapshots/ - +If you are still experiencing this bug, try to check out latest source from https://github.com/php/php-src and re-test. Thank you for the report, and for helping us make PHP better.', 'project' => 'php', 'package_name' => '', @@ -80,7 +27,7 @@ class ReasonRepository 'id' => '5', 'name' => 'fixed', 'status' => 'Closed', - 'title' => 'Fixed in SVN', + 'title' => 'Fix committed', 'message' => 'The fix for this bug has been committed. Since the websites are not directly updated from the repository, the fix might need some time to spread across the globe to all mirror sites, including PHP.net itself. @@ -93,7 +40,7 @@ class ReasonRepository 'id' => '6', 'name' => 'fixed', 'status' => 'Closed', - 'title' => 'Fixed in SVN', + 'title' => 'Fix committed', 'message' => 'This bug has been fixed in the documentation\'s XML sources. Since the online and downloadable versions of the documentation need some time to get updated, we would like to ask you to be a bit patient. @@ -106,7 +53,7 @@ class ReasonRepository 'id' => '7', 'name' => 'fixed', 'status' => 'Closed', - 'title' => 'Fixed in SVN', + 'title' => 'Fix committed', 'message' => 'This bug has been fixed in the documentation\'s XML sources. Since the online and downloadable versions of the documentation need some time to get updated, we would like to ask you to be a bit patient. @@ -119,7 +66,7 @@ class ReasonRepository 'id' => '8', 'name' => 'fixed', 'status' => 'Closed', - 'title' => 'Fixed in SVN', + 'title' => 'Fix committed', 'message' => 'The fix for this bug has been committed. Since the websites are not directly updated from the repository, the fix might need some time to spread across the globe to all mirror sites, including PHP.net itself. @@ -132,8 +79,8 @@ class ReasonRepository 'id' => '9', 'name' => 'fixed', 'status' => 'Closed', - 'title' => 'Fixed in SVN', - 'message' => 'This bug has been fixed in SVN, and should show up online in an + 'title' => 'Fix committed', + 'message' => 'This bug has been fixed and the fix has been committed. It should show up online in an hour or three. Thank you for the report, and for helping us make PHP.net better.', @@ -144,7 +91,7 @@ class ReasonRepository 'id' => '10', 'name' => 'fixed', 'status' => 'Closed', - 'title' => 'Fixed in SVN', + 'title' => 'Fix committed', 'message' => 'The fix for this bug has been committed. Since the PHP Documentation Tools website is updated from the repository at a regular interval, the fix might need some time to take effect. @@ -303,10 +250,17 @@ class ReasonRepository 'webonly' => '0', ], [ 'id' => '22', - 'name' => 'php4', + 'name' => 'phptooold', 'status' => 'Wont fix', - 'title' => 'PHP 4 support discontinued', - 'message' => 'We are sorry, but we can not support PHP 4 related problems anymore.', + 'title' => 'PHP version support discontinued', + 'message' => 'The version of PHP you are reporting on is no longer supported. + Please download a new PHP version from http://www.php.net/downloads.php + + If you are able to reproduce the bug with one of the latest + versions of PHP, please change the PHP version on this bug report + to the version you tested and change the status back to "Open". + Again, thank you for your continued support of PHP. +', 'project' => 'php', 'package_name' => '', 'webonly' => '0', From 3f2d15d3804520fb650daf97605395d8f50531b8 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 2 Sep 2019 16:43:19 +0200 Subject: [PATCH 210/277] Add myself to trusted devs for moderation purposes --- include/trusted-devs.php | 1 + 1 file changed, 1 insertion(+) diff --git a/include/trusted-devs.php b/include/trusted-devs.php index 02f92260..cf9a44ec 100644 --- a/include/trusted-devs.php +++ b/include/trusted-devs.php @@ -19,6 +19,7 @@ 'kalle', 'danbrown', 'nikic', + 'cmb', ]; // Distro people (security related) From 0ed3f812f80a074c641b50e3a8f11c2e5ce94a82 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 6 Sep 2019 09:35:26 +0200 Subject: [PATCH 211/277] Add phpbugreports to spam mail list Same as rhsoft. --- include/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/functions.php b/include/functions.php index d261294c..d5f033a1 100644 --- a/include/functions.php +++ b/include/functions.php @@ -247,7 +247,7 @@ function is_spam($string) /* Primitive check for SPAMmy user. Add more later. */ function is_spam_user($email) { - if (preg_match("/(rhsoft|reindl)/i", $email)) { + if (preg_match("/(rhsoft|reindl|phpbugreports)/i", $email)) { return true; } return false; From ce9c82af403508804a1240f75c0d79940820b79c Mon Sep 17 00:00:00 2001 From: Rasmus Lerdorf Date: Wed, 18 Sep 2019 13:11:38 +0200 Subject: [PATCH 212/277] Check for passed in arrays --- include/query.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/query.php b/include/query.php index da9b683d..507c8e24 100644 --- a/include/query.php +++ b/include/query.php @@ -27,19 +27,19 @@ // Setup input variables.. $boolean_search = isset($_GET['boolean']) ? (int) $_GET['boolean'] : 0; -$status = !empty($_GET['status']) ? $_GET['status'] : 'Open'; -$search_for = !empty($_GET['search_for']) ? $_GET['search_for'] : ''; -$bug_type = (!empty($_GET['bug_type']) && $_GET['bug_type'] != 'All') ? $_GET['bug_type'] : ''; +$status = !empty($_GET['status']) ? (string)$_GET['status'] : 'Open'; +$search_for = !empty($_GET['search_for']) ? (string)$_GET['search_for'] : ''; +$bug_type = (!empty($_GET['bug_type']) && $_GET['bug_type'] != 'All') ? (string)$_GET['bug_type'] : ''; $bug_age = (int) (isset($_GET['bug_age']) ? $_GET['bug_age'] : 0); $bug_updated = (int) (isset($_GET['bug_updated']) ? $_GET['bug_updated'] : 0); -$php_os = !empty($_GET['php_os']) ? $_GET['php_os'] : ''; +$php_os = (!empty($_GET['php_os']) && is_string($_GET['php_os'])) ? $_GET['php_os'] : ''; $php_os_not = !empty($_GET['php_os_not']) ? 'not' : ''; -$phpver = !empty($_GET['phpver']) ? $_GET['phpver'] : ''; -$cve_id = !empty($_GET['cve_id']) ? $_GET['cve_id'] : ''; +$phpver = (!empty($_GET['phpver']) && is_string($_GET['phpver'])) ? $_GET['phpver'] : ''; +$cve_id = (!empty($_GET['cve_id']) && is_string($_GET['cve_id'])) ? $_GET['cve_id'] : ''; $cve_id_not = !empty($_GET['cve_id_not']) ? 'not' : ''; -$patch = !empty($_GET['patch']) ? $_GET['patch'] : ''; -$pull = !empty($_GET['pull']) ? $_GET['pull'] : ''; -$private = !empty($_GET['private']) ? $_GET['private'] : ''; +$patch = (!empty($_GET['patch']) && is_string($_GET['patch'])) ? $_GET['patch'] : ''; +$pull = (!empty($_GET['pull']) && is_string($_GET['pull'])) ? $_GET['pull'] : ''; +$private = (!empty($_GET['private']) && is_string($_GET['private'])) ? $_GET['private'] : ''; $begin = (int) ((!empty($_GET['begin']) && $_GET['begin'] > 0) ? $_GET['begin'] : 0); $limit = (defined('MAX_BUGS_RETURN')) ? MAX_BUGS_RETURN : 30; $project = (!empty($_GET['project']) && $_GET['project'] != 'All') ? $_GET['project'] : ''; @@ -49,7 +49,7 @@ $direction = (!empty($_GET['direction']) && $_GET['direction'] != 'DESC') ? 'ASC' : 'DESC'; $order_by = (!empty($_GET['order_by']) && array_key_exists($_GET['order_by'], $order_options)) ? $_GET['order_by'] : ''; $reorder_by = (!empty($_GET['reorder_by']) && array_key_exists($_GET['reorder_by'], $order_options)) ? $_GET['reorder_by'] : ''; -$assign = !empty($_GET['assign']) ? $_GET['assign'] : ''; +$assign = (!empty($_GET['assign']) && is_string($_GET['assign'])) ? $_GET['assign'] : ''; $author_email = !empty($_GET['author_email']) ? spam_protect($_GET['author_email'], 'reverse') : ''; $package_name = (isset($_GET['package_name']) && is_array($_GET['package_name'])) ? $_GET['package_name'] : []; $package_nname = (isset($_GET['package_nname']) && is_array($_GET['package_nname'])) ? $_GET['package_nname'] : []; From 257c114b05462d5d65ee6efffabd28ad59889b69 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 18 Sep 2019 13:26:20 +0200 Subject: [PATCH 213/277] Fix #78558: Information Exposure Through an Error Message We certainly should not display these details to anybody. It would make sense to show that during development, and maybe to developers. --- include/query.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/query.php b/include/query.php index 507c8e24..2c56dc3a 100644 --- a/include/query.php +++ b/include/query.php @@ -275,7 +275,7 @@ $rows = count($result); $total_rows = $dbh->prepare('SELECT FOUND_ROWS()')->execute()->fetch(\PDO::FETCH_NUM)[0]; } catch (Exception $e) { - $errors[] = 'Invalid query: ' . $e->getMessage(); + $errors[] = 'Invalid query' /*. $e->getMessage() */; } if (defined('MAX_BUGS_RETURN') && $total_rows > $rows) { $warnings[] = 'The search was too general, only ' . MAX_BUGS_RETURN . ' bugs will be returned'; From 44eaf4f6d32c65ae484330bdfd8abe73a77cc9cb Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 20 Oct 2019 16:25:13 +0200 Subject: [PATCH 214/277] Add bukka to security devs list --- include/trusted-devs.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/trusted-devs.php b/include/trusted-devs.php index cf9a44ec..c4bdb97b 100644 --- a/include/trusted-devs.php +++ b/include/trusted-devs.php @@ -70,7 +70,8 @@ 'kalle', 'cmb', 'danbrown', - 'yohgaki' + 'yohgaki', + 'bukka' ]; $security_developers = array_merge($security_developers, $security_distro_people); From 0110b485defe5fb1e8b3b05ce01060f79b4d7b11 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Thu, 19 Dec 2019 10:56:04 +0100 Subject: [PATCH 215/277] Add salathe as trusted dev for moderation purposes --- include/trusted-devs.php | 1 + 1 file changed, 1 insertion(+) diff --git a/include/trusted-devs.php b/include/trusted-devs.php index c4bdb97b..5d020d66 100644 --- a/include/trusted-devs.php +++ b/include/trusted-devs.php @@ -20,6 +20,7 @@ 'danbrown', 'nikic', 'cmb', + 'salathe', ]; // Distro people (security related) From 465b722175908415d049c1f0e2962eca41ec3ea7 Mon Sep 17 00:00:00 2001 From: Rasmus Lerdorf Date: Fri, 20 Mar 2020 05:30:22 -0700 Subject: [PATCH 216/277] Fix return value here --- src/Utils/Versions/Client.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Utils/Versions/Client.php b/src/Utils/Versions/Client.php index bf91a9c1..398840d3 100644 --- a/src/Utils/Versions/Client.php +++ b/src/Utils/Versions/Client.php @@ -28,7 +28,7 @@ public function fetchDevVersions(): array { $json = file_get_contents($this->devVersionsUrl); - return json_decode($json, true); + return json_decode($json, true) ?? []; } /** @@ -38,6 +38,6 @@ public function fetchStableVersions(): array { $json = file_get_contents($this->stableVersionsUrl); - return json_decode($json, true); + return json_decode($json, true) ?? []; } } From b1edeff15793069b460287f71da17667535458b2 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Thu, 11 Jun 2020 17:09:06 +0100 Subject: [PATCH 217/277] They're not learning --- include/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/functions.php b/include/functions.php index d5f033a1..6eb9c102 100644 --- a/include/functions.php +++ b/include/functions.php @@ -247,7 +247,7 @@ function is_spam($string) /* Primitive check for SPAMmy user. Add more later. */ function is_spam_user($email) { - if (preg_match("/(rhsoft|reindl|phpbugreports)/i", $email)) { + if (preg_match("/(rhsoft|reindl|phpbugreports|bugreports@gmail)/i", $email)) { return true; } return false; From 60aa74c6185f2fb59ecc094180743402dc7b28c1 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Fri, 19 Jun 2020 11:45:16 +0100 Subject: [PATCH 218/277] Restrict search links to PHP bugs only; drop PHP 7.1 --- www/index.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/www/index.php b/www/index.php index ce3e68cf..7dd619d8 100644 --- a/www/index.php +++ b/www/index.php @@ -24,12 +24,11 @@ } $searches = [ - 'Most recent open bugs (all)' => '&bug_type=All', - 'Most recent open bugs (all) with patch or pull request' => '&bug_type=All&patch=Y&pull=Y', - 'Most recent open bugs (PHP 7.1)' => '&bug_type=All&phpver=7.1', - 'Most recent open bugs (PHP 7.2)' => '&bug_type=All&phpver=7.2', - 'Most recent open bugs (PHP 7.3)' => '&bug_type=All&phpver=7.3', - 'Most recent open bugs (PHP 7.4)' => '&bug_type=All&phpver=7.4', + 'Most recent open bugs (all)' => '&bug_type=All&project=PHP', + 'Most recent open bugs (all) with patch or pull request' => '&bug_type=All&project=PHP&patch=Y&pull=Y', + 'Most recent open bugs (PHP 7.2)' => '&bug_type=All&phpver=7.2&project=PHP', + 'Most recent open bugs (PHP 7.3)' => '&bug_type=All&phpver=7.3&project=PHP', + 'Most recent open bugs (PHP 7.4)' => '&bug_type=All&phpver=7.4&project=PHP', 'Open Documentation bugs' => '&bug_type=Documentation+Problem', 'Open Documentation bugs (with patches)' => '&bug_type=Documentation+Problem&patch=Y', ]; From f27ce31a9398926894aa9fa5130686e047995711 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 6 Jul 2020 15:16:54 +0200 Subject: [PATCH 219/277] Add helpdeskaustralia to spam list --- include/functions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/include/functions.php b/include/functions.php index 6eb9c102..0cad11e3 100644 --- a/include/functions.php +++ b/include/functions.php @@ -235,6 +235,7 @@ function is_spam($string) 'wholesale', 'fashionretailshop01', 'amoxicillin', + 'helpdeskaustralia', ]; if (preg_match('/\b('. implode('|', $keywords) . ')\b/i', $string)) { From ceb990c913b9187cce986ed9806c15627e7d5f92 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Mon, 20 Jul 2020 10:23:22 +0100 Subject: [PATCH 220/277] Still not learning --- include/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/functions.php b/include/functions.php index 0cad11e3..6e873da4 100644 --- a/include/functions.php +++ b/include/functions.php @@ -248,7 +248,7 @@ function is_spam($string) /* Primitive check for SPAMmy user. Add more later. */ function is_spam_user($email) { - if (preg_match("/(rhsoft|reindl|phpbugreports|bugreports@gmail)/i", $email)) { + if (preg_match("/(rhsoft|reindl|phpbugreports|bugreports\d*@gmail)/i", $email)) { return true; } return false; From a7c206cc24968080c21e626e176745f8265646b2 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Wed, 5 Aug 2020 17:55:31 +0100 Subject: [PATCH 221/277] Oh please do fork off --- include/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/functions.php b/include/functions.php index 6e873da4..c61900ea 100644 --- a/include/functions.php +++ b/include/functions.php @@ -248,7 +248,7 @@ function is_spam($string) /* Primitive check for SPAMmy user. Add more later. */ function is_spam_user($email) { - if (preg_match("/(rhsoft|reindl|phpbugreports|bugreports\d*@gmail)/i", $email)) { + if (preg_match("/(rhsoft|reindl|phpbugreports|bugreprtsz|bugreports\d*@gmail)/i", $email)) { return true; } return false; From 53b60e33bb2d5f9f92f664f029a97d400a359c6a Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 21 Oct 2020 10:53:28 +0200 Subject: [PATCH 222/277] Rewrite generate backtrace on Windows w/o compiler section That info was terribly outdated; we provide up-to-date instructions instead. --- .../pages/bugs_generating_backtrace_win32.php | 78 ++++++------------ www/images/backtrace-images-win32/analyze.jpg | Bin 18007 -> 0 bytes .../backtrace-images-win32/dbg_options.png | Bin 24020 -> 0 bytes .../backtrace-images-win32/dbg_select_php.png | Bin 15821 -> 0 bytes .../backtrace-images-win32/dbg_wizard_1.png | Bin 16816 -> 0 bytes .../backtrace-images-win32/dbg_wizard_2.png | Bin 18695 -> 0 bytes www/images/backtrace-images-win32/rules.jpg | Bin 18795 -> 0 bytes 7 files changed, 24 insertions(+), 54 deletions(-) delete mode 100644 www/images/backtrace-images-win32/analyze.jpg delete mode 100644 www/images/backtrace-images-win32/dbg_options.png delete mode 100644 www/images/backtrace-images-win32/dbg_select_php.png delete mode 100644 www/images/backtrace-images-win32/dbg_wizard_1.png delete mode 100644 www/images/backtrace-images-win32/dbg_wizard_2.png delete mode 100644 www/images/backtrace-images-win32/rules.jpg diff --git a/templates/pages/bugs_generating_backtrace_win32.php b/templates/pages/bugs_generating_backtrace_win32.php index 985be241..698b8489 100644 --- a/templates/pages/bugs_generating_backtrace_win32.php +++ b/templates/pages/bugs_generating_backtrace_win32.php @@ -38,68 +38,38 @@ KERNEL32! 77e81af6() -

    Generating backtrace, without compiler, on Windows

    -

    You'll need:

    - +

    Generating a backtrace without compiler is usually a two step process:

    +

    There are several solutions for either step; in the following we describe one solution each.

    -

    For the sake of this example, we will simply use PHP in the shell. The same -method can be used for IIS or any other process or services.

    - - -

    Once you have installed the Debug diagnostic tools and uncompressed PHP and -its debug pack (they can be kept in two separate folders), the first step is to -configure the diagnostic tools. Select the tools menu and click on "Options and -settings". The first tab contains the path to the symbols files, add the "debug -folder" to the existing list using the "browse" button:

    - -

    Options

    - -

    Now we are ready to generate our backtrace.

    - -

    We will use the wizard, click the "Add a rule" button and choose "Crash" as -the rule type:

    -

    Wizard #1

    - -

    In the next window, select "a specific process":

    - -

    Wizard #2

    - -

    Add a "sleep(10);" for the first run (from the cmd: "php.exe crashme.php"), -it will let you enough time to click "next" and select the php process. If you -are debugging the Apache module, start Apache with -X option and choose -httpd.exe instead of php.exe from the process list. Then proceed further:

    +

    Use ProcDump to generate crash dump files

    -

    Select the php process

    +

    Download ProcDump, +and register it as the Just-in-Time (AeDebug) debugger:

    +
    procdump -ma -i C:\dumps
    +

    Use any suitable folder to store the crash dump files instead of C:\dumps. +Whenever a process crashes, a crash dump file will be written to the specified folder.

    -

    Click again next and let it crash. If everything went well, you should see -your new rule as shown in the image below:

    - -

    rules list

    - -

    It also detected that "php.exe" was used. A rule has been created for all -instance of "php.exe". It will save you the sleep and process selection.

    - -

    Now you can click the "Analyze data" button:

    - -

    Analyze

    - -

    Et voila, the complete report will show up in your internet explorer -(compressed html):

    +

    Use Debug Diagnostic Tool to analyze the crash dump

    +
      +
    • Download and install Debug Diagnostic Tool
    • +
    • Download and unpack the PHP debug pack corresponding to the PHP version you are running
    • +
    • Start the Debug Diagnostic Tool
    • +
    • Add the path to the unpacked PHP debug pack as symbol search path in the settings
    • +
    • Press "add data file" and select the crash dump file formerly generated
    • +
    • Select "default analysis"
    • +
    • Press "start analysis"
    • +
    +

    After the analysis has finished, the DebugDiag Analysis Report opens in Internet +Explorer; the relevant part of that report is the stack backtrace, which looks similar +to the following:

    Debug report backtrace screenshot

    -

    What we need is the backtrace itself which can be found under "Thread X - -System ID XXX".

    - end('content') ?> diff --git a/www/images/backtrace-images-win32/analyze.jpg b/www/images/backtrace-images-win32/analyze.jpg deleted file mode 100644 index 8fc4baa66b80683eada847743466770ad971b1e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18007 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 diff --git a/www/images/backtrace-images-win32/dbg_options.png b/www/images/backtrace-images-win32/dbg_options.png deleted file mode 100644 index f0a0d6117d18546869f89dd0f6dd6d030bfa8b4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24020 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 YpRjrceKvoDqA);CN=dR({L|O}0RhyeFaQ7m diff --git a/www/images/backtrace-images-win32/dbg_wizard_1.png b/www/images/backtrace-images-win32/dbg_wizard_1.png deleted file mode 100644 index 6ef55170b5a443e8ace31fdf1f48b2a4166f08a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16816 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 V%md!{&k6tl002ovPDHLkV1i_&YuNw* diff --git a/www/images/backtrace-images-win32/rules.jpg b/www/images/backtrace-images-win32/rules.jpg deleted file mode 100644 index 1c60ae95e0c34ec80dc56b0697ade00da1e7875b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18795 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 From 11c6f02ed162ee72e999ee06c8c539f15f7b711f Mon Sep 17 00:00:00 2001 From: Gabriel Caruso Date: Tue, 3 Nov 2020 11:24:40 +0100 Subject: [PATCH 223/277] Add PHP 8.0 to common searches Closes GH-87. --- www/index.php | 1 + 1 file changed, 1 insertion(+) diff --git a/www/index.php b/www/index.php index 7dd619d8..a1461a72 100644 --- a/www/index.php +++ b/www/index.php @@ -29,6 +29,7 @@ 'Most recent open bugs (PHP 7.2)' => '&bug_type=All&phpver=7.2&project=PHP', 'Most recent open bugs (PHP 7.3)' => '&bug_type=All&phpver=7.3&project=PHP', 'Most recent open bugs (PHP 7.4)' => '&bug_type=All&phpver=7.4&project=PHP', + 'Most recent open bugs (PHP 8.0)' => '&bug_type=All&phpver=8.0&project=PHP', 'Open Documentation bugs' => '&bug_type=Documentation+Problem', 'Open Documentation bugs (with patches)' => '&bug_type=Documentation+Problem&patch=Y', ]; From 2e6915b67db5fbb3179b54abb6dae1474b6b0b88 Mon Sep 17 00:00:00 2001 From: Kevin Reinders <30635447+GreeenPeppper@users.noreply.github.com> Date: Mon, 28 Dec 2020 14:15:43 +0100 Subject: [PATCH 224/277] Fix #80550: bug search: Nonsensical "the following words were ignored:" w/ trailing space We `trim()` that input. Closes GH-88. --- include/query.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/query.php b/include/query.php index 2c56dc3a..8318747c 100644 --- a/include/query.php +++ b/include/query.php @@ -28,7 +28,7 @@ // Setup input variables.. $boolean_search = isset($_GET['boolean']) ? (int) $_GET['boolean'] : 0; $status = !empty($_GET['status']) ? (string)$_GET['status'] : 'Open'; -$search_for = !empty($_GET['search_for']) ? (string)$_GET['search_for'] : ''; +$search_for = !empty($_GET['search_for']) ? trim((string)$_GET['search_for']) : ''; $bug_type = (!empty($_GET['bug_type']) && $_GET['bug_type'] != 'All') ? (string)$_GET['bug_type'] : ''; $bug_age = (int) (isset($_GET['bug_age']) ? $_GET['bug_age'] : 0); $bug_updated = (int) (isset($_GET['bug_updated']) ? $_GET['bug_updated'] : 0); From 997f010b1f66fe7ea1af819f051681300d18e675 Mon Sep 17 00:00:00 2001 From: Peter Cowburn Date: Mon, 4 Jan 2021 11:33:01 +0000 Subject: [PATCH 225/277] another spam word --- include/functions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/include/functions.php b/include/functions.php index c61900ea..29e679cb 100644 --- a/include/functions.php +++ b/include/functions.php @@ -236,6 +236,7 @@ function is_spam($string) 'fashionretailshop01', 'amoxicillin', 'helpdeskaustralia', + 'porn', ]; if (preg_match('/\b('. implode('|', $keywords) . ')\b/i', $string)) { From 1795030d73decb454aae11fa5d32fe966595b44b Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 20 Jan 2021 15:10:46 +0100 Subject: [PATCH 226/277] Add Pieter Hordijk as trusted dev --- include/trusted-devs.php | 1 + 1 file changed, 1 insertion(+) diff --git a/include/trusted-devs.php b/include/trusted-devs.php index 5d020d66..b63dae0e 100644 --- a/include/trusted-devs.php +++ b/include/trusted-devs.php @@ -21,6 +21,7 @@ 'nikic', 'cmb', 'salathe', + 'peehaa', ]; // Distro people (security related) From 860d1552b51c21531083297f160db938f1a3e945 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 15 Mar 2021 09:48:24 +0100 Subject: [PATCH 227/277] Add aarinkaur to spam list Large number of spam comments posted today. --- include/functions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/include/functions.php b/include/functions.php index 29e679cb..c46d9179 100644 --- a/include/functions.php +++ b/include/functions.php @@ -237,6 +237,7 @@ function is_spam($string) 'amoxicillin', 'helpdeskaustralia', 'porn', + 'aarinkaur', ]; if (preg_match('/\b('. implode('|', $keywords) . ')\b/i', $string)) { From 3f8314260cb3cbc7e697e57e2246a11de8f6d9eb Mon Sep 17 00:00:00 2001 From: Rasmus Lerdorf Date: Mon, 29 Mar 2021 05:27:19 -0700 Subject: [PATCH 228/277] Remove references to git.php.net --- README.md | 5 +---- composer.json | 2 +- src/Utils/Versions/Generator.php | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1adab68d..d5096633 100644 --- a/README.md +++ b/README.md @@ -74,10 +74,7 @@ Issues with the application and new feature requests can be reported to php-webmaster@lists.php.net. Application source code is located in the -[git.php.net](https://git.php.net/?p=web/bugs.git) repository. - -Contributions can be done by forking the [GitHub mirror](https://github.com/php/web-bugs) -repository and sending a pull request. +[github.com/php/web-bugs](https://github.com/php/web-bugs) repository. ```bash git clone git@github.com:your-username/web-bugs diff --git a/composer.json b/composer.json index abec91de..6bee75dd 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "issues": "https://bugs.php.net/", "wiki": "https://wiki.php.net/", "irc": "irc://irc.efnet.org/php.pecl", - "source": "https://git.php.net/?p=web/bugs.git", + "source": "https://github.com/php/web-bugs", "docs": "https://php.net/manual", "rss": "https://bugs.php.net/rss" }, diff --git a/src/Utils/Versions/Generator.php b/src/Utils/Versions/Generator.php index 0631c460..854c786a 100644 --- a/src/Utils/Versions/Generator.php +++ b/src/Utils/Versions/Generator.php @@ -13,7 +13,7 @@ * are pulled from the https://qa.php.net. * * To add a new PHP version add it to: - * https://git.php.net/?p=web/qa.git;a=blob;f=include/release-qa.php + * https://github.com/php/web-qa/blob/master/include/release-qa.php * * The versions are weighted by the following criteria: * - major+minor version desc (7>5.4>5.3>master) From c424ea4c31a54e08563bb95fcb2b35ef0dcb1515 Mon Sep 17 00:00:00 2001 From: Rasmus Lerdorf Date: Mon, 29 Mar 2021 05:55:46 -0700 Subject: [PATCH 229/277] Fix README (and test signing sub-key) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d5096633..d0bef4e0 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ php-webmaster@lists.php.net. Application source code is located in the [github.com/php/web-bugs](https://github.com/php/web-bugs) repository. +To contribute: + ```bash git clone git@github.com:your-username/web-bugs cd web-bugs From 867d92058759ff0368a0c91c9c68849f40f4a8f0 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 31 Mar 2021 21:12:40 +0200 Subject: [PATCH 230/277] Add forgot password link --- www/login.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/login.php b/www/login.php index b5f6b523..21e833f1 100644 --- a/www/login.php +++ b/www/login.php @@ -24,7 +24,7 @@ redirect('index.php'); } else { ?> -
    Wrong username or password!
    +
    Wrong username or password! Forgot your password?
    Date: Mon, 5 Apr 2021 22:56:19 +0200 Subject: [PATCH 231/277] Directly check token for rpc.php Make this code independent of user authentication by checking for a hardcoded token. --- www/rpc.php | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/www/rpc.php b/www/rpc.php index b6086c73..c532027f 100644 --- a/www/rpc.php +++ b/www/rpc.php @@ -19,20 +19,13 @@ // Obtain common includes require_once '../include/prepend.php'; -if (isset($_POST['MAGIC_COOKIE'])) { - list($user, $pwd) = explode(":", base64_decode($_POST['MAGIC_COOKIE']), 2); - $auth_user = new stdClass; - $auth_user->handle = $user; - $auth_user->password = $pwd; -} else { - echo json_encode(['result' => ['error' => 'Missing credentials']]); +if (!isset($_POST['MAGIC_COOKIE'])) { + echo json_encode(['result' => ['error' => 'Missing token']]); exit; } -bugs_authenticate($user, $pwd, $logged_in, $user_flags); - -if (empty($auth_user->handle)) { - echo json_encode(['result' => ['error' => 'Invalid user or password']]); +if (sha1($_POST['MAGIC_COOKIE']) !== '8514f801cfba2ec74ec08264567ba291485f2765') { + echo json_encode(['result' => ['error' => 'Invalid token']]); exit; } @@ -45,7 +38,8 @@ exit; } -if (!bugs_has_access($bug_id, $bug, $pwd, $user_flags)) { +// Be conservative: Do not allow access to private bugs. +if ($bug['private'] === 'Y') { echo json_encode(['result' => ['error' => 'No access to bug']]); exit; } From 4f2b72f6265c74e6a26a1e2a58ae6b37d7325f5a Mon Sep 17 00:00:00 2001 From: Rasmus Lerdorf Date: Mon, 5 Apr 2021 21:58:09 -0700 Subject: [PATCH 232/277] master->main --- include/functions.php | 2 +- www/js/userlisting.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/functions.php b/include/functions.php index c46d9179..f52f5c7d 100644 --- a/include/functions.php +++ b/include/functions.php @@ -79,7 +79,7 @@ function verify_user_password($user, $pass) $ctx = stream_context_create(['http' => $opts]); - $s = file_get_contents('https://master.php.net/fetch/cvsauth.php', false, $ctx); + $s = file_get_contents('https://main.php.net/fetch/cvsauth.php', false, $ctx); $a = @unserialize($s); if (!is_array($a)) { diff --git a/www/js/userlisting.php b/www/js/userlisting.php index 36c1461d..bca78504 100644 --- a/www/js/userlisting.php +++ b/www/js/userlisting.php @@ -9,7 +9,7 @@ function getAllUsers() $ctx = stream_context_create(['http' => $opts]); $token = getenv('USER_TOKEN'); - $retval = @file_get_contents('https://master.php.net/fetch/allusers.php?&token=' . rawurlencode($token), false, $ctx); + $retval = @file_get_contents('https://main.php.net/fetch/allusers.php?&token=' . rawurlencode($token), false, $ctx); if (!$retval) { return; From 3a2b04358544336d36e73006a47175d6afb83f1f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 6 Apr 2021 15:37:55 +0200 Subject: [PATCH 233/277] Point forgot password link to main --- www/login.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/login.php b/www/login.php index 21e833f1..6c3cd0b6 100644 --- a/www/login.php +++ b/www/login.php @@ -24,7 +24,7 @@ redirect('index.php'); } else { ?> -
    Wrong username or password! Forgot your password?
    +
    Wrong username or password! Forgot your password?
    Date: Fri, 23 Jul 2021 17:49:15 +0800 Subject: [PATCH 234/277] Update style (#93) --- www/css/style.css | 1 - 1 file changed, 1 deletion(-) diff --git a/www/css/style.css b/www/css/style.css index 743ea143..d0339383 100644 --- a/www/css/style.css +++ b/www/css/style.css @@ -374,7 +374,6 @@ td.content { td.content table { font-size: 100%; /* make IE look normal */ - padding-bottom: 15px; } .headerbottom { From d13a5fe37024bef133b7a93aa45511c3bcbafa78 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 25 Jul 2021 17:37:01 +0200 Subject: [PATCH 235/277] Fix #81292: Issue tracker: How to Search claims advanced search defaults Find bugs to "all" --- templates/pages/search_how_to.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/pages/search_how_to.php b/templates/pages/search_how_to.php index fb5189c6..4bf76fab 100644 --- a/templates/pages/search_how_to.php +++ b/templates/pages/search_how_to.php @@ -41,8 +41,8 @@ subject, and description. Minimum term length is three characters. There are three types of searches:
      -
    • all : (default) All search terms are required.
    • -
    • any : One or more (any) of the search terms may be present.
    • +
    • all : All search terms are required.
    • +
    • any : (default) One or more (any) of the search terms may be present.
    • raw : Allows full use of MySQL's FULLTEXT From 8061e3a0be8f67dcc44f102ea66305e4fbfb300d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=88=E5=94=81?= <52o@qq52o.cn> Date: Wed, 18 Aug 2021 10:08:20 +0800 Subject: [PATCH 236/277] Update ReasonRepository.php --- src/Repository/ReasonRepository.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Repository/ReasonRepository.php b/src/Repository/ReasonRepository.php index d5c7cf13..6c436d39 100644 --- a/src/Repository/ReasonRepository.php +++ b/src/Repository/ReasonRepository.php @@ -254,12 +254,12 @@ class ReasonRepository 'status' => 'Wont fix', 'title' => 'PHP version support discontinued', 'message' => 'The version of PHP you are reporting on is no longer supported. - Please download a new PHP version from http://www.php.net/downloads.php +Please download a new PHP version from http://www.php.net/downloads.php - If you are able to reproduce the bug with one of the latest - versions of PHP, please change the PHP version on this bug report - to the version you tested and change the status back to "Open". - Again, thank you for your continued support of PHP. +If you are able to reproduce the bug with one of the latest +versions of PHP, please change the PHP version on this bug report +to the version you tested and change the status back to "Open". +Again, thank you for your continued support of PHP. ', 'project' => 'php', 'package_name' => '', From f259231094cc03c490903e55d8b40ad4db96621f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 26 Aug 2021 14:22:04 +0200 Subject: [PATCH 237/277] Count https:// URLs towards spam --- include/functions.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/functions.php b/include/functions.php index f52f5c7d..7c4841a1 100644 --- a/include/functions.php +++ b/include/functions.php @@ -195,7 +195,9 @@ function is_spam($string) return false; } - if (substr_count(strtolower($string), 'http://') > 5) { + $count = substr_count(strtolower($string), 'http://') + + substr_count(strtolower($string), 'https://'); + if ($count > 5) { return true; } From 3bccfcba4fbd45d69d13765e82779cd1841fd285 Mon Sep 17 00:00:00 2001 From: Peter Cowburn Date: Wed, 1 Sep 2021 10:27:56 +0100 Subject: [PATCH 238/277] add mikemike as trusted dev --- include/trusted-devs.php | 1 + 1 file changed, 1 insertion(+) diff --git a/include/trusted-devs.php b/include/trusted-devs.php index b63dae0e..bdb9cc48 100644 --- a/include/trusted-devs.php +++ b/include/trusted-devs.php @@ -22,6 +22,7 @@ 'cmb', 'salathe', 'peehaa', + 'mikemike', ]; // Distro people (security related) From 4bc692453de3ff7731f00fb103408d66ed056c76 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 1 Sep 2021 20:24:12 +0200 Subject: [PATCH 239/277] Add lildurk to spam list --- include/functions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/include/functions.php b/include/functions.php index 7c4841a1..a2ef5546 100644 --- a/include/functions.php +++ b/include/functions.php @@ -240,6 +240,7 @@ function is_spam($string) 'helpdeskaustralia', 'porn', 'aarinkaur', + 'lildurk', ]; if (preg_match('/\b('. implode('|', $keywords) . ')\b/i', $string)) { From 98896cb95e905892d929bf8fc240c004ae25c52c Mon Sep 17 00:00:00 2001 From: peterdd Date: Wed, 1 Sep 2021 23:29:39 +0200 Subject: [PATCH 240/277] fix a fatal error when passed null to e() Fatal error: Uncaught TypeError: Argument 1 passed to App\Template\Context::e() must be of the type string, null given, called in /srv/www/htdocs/web-bugs/templates/pages/admin/database_status.php on line 64 and defined in /srv/www/htdocs/web-bugs/src/Template/Context.php:159 --- templates/pages/admin/database_status.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/pages/admin/database_status.php b/templates/pages/admin/database_status.php index a1237cee..16aed897 100644 --- a/templates/pages/admin/database_status.php +++ b/templates/pages/admin/database_status.php @@ -61,7 +61,7 @@
    - + From 8bf14f324bce9cb4c227aabc46f28d4b038cf916 Mon Sep 17 00:00:00 2001 From: peterdd Date: Fri, 3 Sep 2021 10:04:30 +0200 Subject: [PATCH 241/277] recent bugs stat floating table fix (#97) * recent bugs stat tables better float using display:inline-block; or flex layouts provide better method than using historic the float:left approach. * css for recent bugs stat tables Using display:inline-block; is better than using float:left; --- www/css/style.css | 6 ++++++ www/stats.php | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/www/css/style.css b/www/css/style.css index d0339383..293147a3 100644 --- a/www/css/style.css +++ b/www/css/style.css @@ -273,6 +273,12 @@ a.bug_stats_choosen { background-color: #aabbcc; } +.bugstatrecent table { + display:inline-block; + vertical-align:top; + margin-right:1em; + margin-top:1em; +} /* ========= Other ========= */ a:link { diff --git a/www/stats.php b/www/stats.php index aaac6886..0a397366 100644 --- a/www/stats.php +++ b/www/stats.php @@ -133,7 +133,9 @@ } } -echo "
    $column
    $value
    e($tableStatus['Create_time']); ?>e($tableStatus['Update_time']); ?>e($tableStatus['Update_time']?:''); ?> e($tableStatus['Check_time']) : ''; ?> e($tableStatus['Collation']); ?> e($tableStatus['Checksum']) : ''; ?>
    \n
    \n

    PHP Versions for recent bug reports:

    "; +echo "\n
    \n

    PHP Versions for recent bug reports:

    "; + +echo '
    '; $last_date = null; foreach ($bugRepository->findPhpVersions($bug_type) as $row) { @@ -141,7 +143,7 @@ if ($last_date !== null) { echo "\n\n"; } - echo "\n". + echo "
    \n". "\n"; $last_date = $row['d']; } From 5589a716dc3b691eb6bdd8ba74c8d4389642ada6 Mon Sep 17 00:00:00 2001 From: peterdd Date: Fri, 17 Sep 2021 13:10:49 +0200 Subject: [PATCH 242/277] Make multiple selects resizable Just wanted report a bug on bugs.php.net, but letting the user scroll 460 entries (optgroups+options) in a 6 line height tiny multiselect in the bug search is torture. Closes GH-84. --- www/css/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/www/css/style.css b/www/css/style.css index 293147a3..aa87f59c 100644 --- a/www/css/style.css +++ b/www/css/style.css @@ -156,6 +156,10 @@ input { font-family: verdana,arial,helvetica,sans-serif; } +select { + resize: both; +} + textarea { font-family: "andale mono", "monotype.com", "courier new", monospace; } From 7febedd645d84fabf3ba5c3597694cad7fcd56c8 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Fri, 17 Sep 2021 13:16:18 +0200 Subject: [PATCH 243/277] Add Kamil Tekiela as trusted dev Closes GH-89. --- include/trusted-devs.php | 1 + 1 file changed, 1 insertion(+) diff --git a/include/trusted-devs.php b/include/trusted-devs.php index bdb9cc48..29a20d33 100644 --- a/include/trusted-devs.php +++ b/include/trusted-devs.php @@ -23,6 +23,7 @@ 'salathe', 'peehaa', 'mikemike', + 'dharman', ]; // Distro people (security related) From 2f06b4d7bb4c42311fb2ac9154f10438b800d697 Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Mon, 27 Sep 2021 14:42:13 +0000 Subject: [PATCH 244/277] Add X-Frame-Options: SAMEORIGIN --- include/functions.php | 1 + templates/layout.php | 1 + 2 files changed, 2 insertions(+) diff --git a/include/functions.php b/include/functions.php index a2ef5546..b72c2c8c 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1514,6 +1514,7 @@ function response_header($title, $extraHeaders = '') $_header_done = true; header('Content-Type: text/html; charset=UTF-8'); + header('X-Frame-Options: SAMEORIGIN'); ?> diff --git a/templates/layout.php b/templates/layout.php index a0eb35f5..35a400e1 100644 --- a/templates/layout.php +++ b/templates/layout.php @@ -5,6 +5,7 @@ PHP :: <?= $this->e($title) ?> +
    {$row["d"]}
    From 90f86d6ab964d58d9f2f454d591d40b868ee75a4 Mon Sep 17 00:00:00 2001 From: peterdd Date: Wed, 6 Oct 2021 21:31:50 +0200 Subject: [PATCH 245/277] add css classes to status select in search form (#106) Reuses the existing CSS classes to apply the same backgorund colors as in the bug search result table rows. I know that Chrome and Safari ignore applying a background-color to option tags of a single select. But when bugs.php.net supports also multiselect for the status select both will show the background-color in multi selects (multiple="multiple" attribute for xhtml compatible modus) --- include/functions.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/include/functions.php b/include/functions.php index b72c2c8c..e0e3b6fb 100644 --- a/include/functions.php +++ b/include/functions.php @@ -495,7 +495,7 @@ function show_type_options($current = 'Bug', $all = false) */ function show_state_options($state, $user_mode = 0, $default = '', $assigned = 0) { - global $state_types; + global $state_types, $tla; if (!$state && !$default) { $state = $assigned ? 'Assigned' : 'Open'; @@ -512,21 +512,25 @@ function show_state_options($state, $user_mode = 0, $default = '', $assigned = 0 */ case 'Feedback': if ($assigned) { - echo "\n"; + echo ''."\n"; } else { - echo "\n"; + echo ''."\n"; } break; case 'No Feedback': - echo "\n"; + echo ''."\n"; break; default: - echo "\n"; + echo ''.$state.''."\n"; break; } /* Allow state 'Closed' always when current state is not 'Not a bug' */ if ($state != 'Not a bug') { - echo "\n"; + echo ''."\n"; } } else { foreach($state_types as $type => $mode) { @@ -536,6 +540,9 @@ function show_state_options($state, $user_mode = 0, $default = '', $assigned = 0 } if ($mode >= $user_mode) { echo ' Date: Thu, 7 Oct 2021 15:52:25 +0200 Subject: [PATCH 246/277] Add tvfun to spam list --- include/functions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/include/functions.php b/include/functions.php index e0e3b6fb..1816d005 100644 --- a/include/functions.php +++ b/include/functions.php @@ -241,6 +241,7 @@ function is_spam($string) 'porn', 'aarinkaur', 'lildurk', + 'tvfun', ]; if (preg_match('/\b('. implode('|', $keywords) . ')\b/i', $string)) { From 5853f7850c5c8b4711b110f0518c5c7be717abbf Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Sun, 17 Oct 2021 20:46:49 -0700 Subject: [PATCH 247/277] Add Stas to trusted dev list to clean up spam --- include/trusted-devs.php | 1 + 1 file changed, 1 insertion(+) diff --git a/include/trusted-devs.php b/include/trusted-devs.php index 29a20d33..4209e5a8 100644 --- a/include/trusted-devs.php +++ b/include/trusted-devs.php @@ -24,6 +24,7 @@ 'peehaa', 'mikemike', 'dharman', + 'stas', ]; // Distro people (security related) From c2a59a52fa60c5abc8e3b9c573e3a1d63a16b4c9 Mon Sep 17 00:00:00 2001 From: peterdd Date: Sat, 18 Sep 2021 22:14:21 +0200 Subject: [PATCH 248/277] fix typo in tests/bootstrap.php I had to fix the path there to enable running the phpunit tests. --- tests/bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index f6b6db98..997c9f7a 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -2,6 +2,6 @@ require_once __DIR__ . '/../vendor/autoload.php'; -define('TEST_FIXTURES_DIRECTORY', __DIR__ . '/Fixtures'); +define('TEST_FIXTURES_DIRECTORY', __DIR__ . '/fixtures'); define('TEST_MOCKS_DIRECTORY', __DIR__ . '/Mocks'); define('TEST_VAR_DIRECTORY', __DIR__ . '/../var'); From 49be135fd809a72746539cea56d9221d01c860ad Mon Sep 17 00:00:00 2001 From: Danack Date: Mon, 8 Feb 2021 10:02:24 +0000 Subject: [PATCH 249/277] Group by needs to specifically reference a column name, not a variable. --- src/Repository/BugRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Repository/BugRepository.php b/src/Repository/BugRepository.php index 8bb871fb..dadbb997 100644 --- a/src/Repository/BugRepository.php +++ b/src/Repository/BugRepository.php @@ -45,7 +45,7 @@ public function findOneById(int $id): array FROM bugdb b LEFT JOIN bugdb_votes ON b.id = bug WHERE b.id = ? - GROUP BY bug + GROUP BY b.id '; $statement = $this->dbh->prepare($sql); From 2edddded73ce8d7258cec3311f4e993b96a2ee48 Mon Sep 17 00:00:00 2001 From: peterdd Date: Fri, 24 Sep 2021 19:35:49 +0200 Subject: [PATCH 250/277] Check if params for reporting a bug are is_string() Closes GH-104. --- include/functions.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/include/functions.php b/include/functions.php index 1816d005..a380cee8 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1158,9 +1158,11 @@ function is_valid_email($email, $phpnet_allowed = true) /** * Validate an incoming bug report * - * @param + * @param mixed $in usually $_POST['in'] + * @param bool $initial + * @param bool $logged_in * - * @return void + * @return array */ function incoming_details_are_valid($in, $initial = 0, $logged_in = false) { @@ -1176,7 +1178,8 @@ function incoming_details_are_valid($in, $initial = 0, $logged_in = false) $errors[] = 'Please provide a valid email address.'; } } - if (!$logged_in && $initial && empty($in['passwd'])) { + + if (!$logged_in && $initial && (empty($in['passwd']) || !is_string($in['passwd']))) { $errors[] = 'Please provide a password for this bug report.'; } @@ -1184,25 +1187,25 @@ function incoming_details_are_valid($in, $initial = 0, $logged_in = false) $errors[] = 'Please select a valid PHP version. If your PHP version is too old, please upgrade first and see if the problem has not already been fixed.'; } - if (empty($in['php_version']) || ($initial && !in_array($in['php_version'], $versions))) { + if (empty($in['php_version']) || !is_string($in['php_version']) || ($initial && !in_array($in['php_version'], $versions))) { $errors[] = 'Please select a valid PHP version.'; } - if (empty ($in['package_name']) || $in['package_name'] == 'none') { + if (empty($in['package_name']) || !is_string($in['package_name']) || $in['package_name'] == 'none') { $errors[] = 'Please select an appropriate package.'; } else if (!package_exists($in['package_name'])) { $errors[] = 'Please select an appropriate package.'; } - if (empty($in['bug_type']) || !array_key_exists($in['bug_type'], $bug_types)) { + if (empty($in['bug_type']) || !is_string($in['bug_type']) || !array_key_exists($in['bug_type'], $bug_types)) { $errors[] = 'Please select a valid bug type.'; } - if (empty($in['sdesc'])) { + if (empty($in['sdesc']) || !is_string($in['sdesc'])) { $errors[] = 'You must supply a short description of the bug you are reporting.'; } - if ($initial && empty($in['ldesc'])) { + if ($initial && (empty($in['ldesc']) || !is_string($in['ldesc']))) { $errors[] = 'You must supply a long description of the bug you are reporting.'; } From 7dac397fc20fa76d9d8aa352dcc5b981a871a5b7 Mon Sep 17 00:00:00 2001 From: peterdd Date: Sat, 18 Sep 2021 17:56:50 +0200 Subject: [PATCH 251/277] top align selects on report.php With the new flexibility of resizing the package category select the appearing of the 2 selects side by side on https://bugs.php.net/report.php might look a bit odd. This PR fixes it by align the 2 selects always to the top. --- www/css/style.css | 1 + 1 file changed, 1 insertion(+) diff --git a/www/css/style.css b/www/css/style.css index aa87f59c..4423814b 100644 --- a/www/css/style.css +++ b/www/css/style.css @@ -158,6 +158,7 @@ input { select { resize: both; + vertical-align:top; } textarea { From 1062abe4989226065188931b4c2402a7d8b3ed06 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 21 Oct 2021 11:56:12 +0200 Subject: [PATCH 252/277] Fix JS error when resizing package group select If nothing is selected, we should skip all the updating code. --- www/js/package-affected.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/www/js/package-affected.js b/www/js/package-affected.js index 4f2e58fb..227f8b09 100644 --- a/www/js/package-affected.js +++ b/www/js/package-affected.js @@ -48,6 +48,10 @@ window.addEventListener( ); function updateGroup() { + if (!packageGroup.value) { + return; + } + select.disabled = false; if (instructions instanceof HTMLElement) { From 39ce725941f996c9aaaa1411580a22f6bf27c0b9 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 21 Oct 2021 12:03:58 +0200 Subject: [PATCH 253/277] Update composer.lock Avoids phpunit deprecation warnings --- composer.lock | 822 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 563 insertions(+), 259 deletions(-) diff --git a/composer.lock b/composer.lock index 81116187..bcfb87d8 100644 --- a/composer.lock +++ b/composer.lock @@ -9,36 +9,31 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.2.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^6.0", + "doctrine/coding-standard": "^8.0", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.13", - "phpstan/phpstan-phpunit": "^0.11", - "phpstan/phpstan-shim": "^0.11", - "phpunit/phpunit": "^7.0" + "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" @@ -52,7 +47,7 @@ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "homepage": "https://ocramius.github.io/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", @@ -61,24 +56,42 @@ "constructor", "instantiate" ], - "time": "2019-03-17T17:37:11+00:00" + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-11-10T18:47:58+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.1", + "version": "1.10.2", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72" + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", - "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "replace": { "myclabs/deep-copy": "self.version" @@ -109,32 +122,43 @@ "object", "object graph" ], - "time": "2019-04-07T13:18:21+00:00" + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-11-13T09:40:50+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.3", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -164,24 +188,28 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08T19:23:20+00:00" + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" }, { "name": "phar-io/version", - "version": "2.0.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + "reference": "bae7c545bef187884426f042434e561ab1ddb182" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -211,39 +239,38 @@ } ], "description": "Library for handling version information and constraints", - "time": "2018-07-08T19:19:57+00:00" + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.1.0" + }, + "time": "2021-02-23T14:00:09+00:00" }, { "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -265,44 +292,46 @@ "reflection", "static analysis" ], - "time": "2017-09-11T18:02:19+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.1", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c" + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", - "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", "shasum": "" }, "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" }, "require-dev": { - "doctrine/instantiator": "~1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -313,44 +342,50 @@ { "name": "Mike van Riel", "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2019-04-30T17:48:53+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "ext-tokenizer": "*", + "psalm/phar": "^4.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -363,42 +398,47 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14T14:27:02+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1" + }, + "time": "2021-10-02T14:08:47+00:00" }, { "name": "phpspec/prophecy", - "version": "1.8.0", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.2", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "phpspec/phpspec": "^6.0 || ^7.0", + "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -426,39 +466,43 @@ "spy", "stub" ], - "time": "2018-08-05T17:53:17+00:00" + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/1.14.0" + }, + "time": "2021-09-10T09:02:12+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "7.0.3", + "version": "7.0.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "0317a769a81845c390e19684d9ba25d7f6aa4707" + "reference": "819f92bba8b001d4363065928088de22f25a3a48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0317a769a81845c390e19684d9ba25d7f6aa4707", - "reference": "0317a769a81845c390e19684d9ba25d7f6aa4707", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/819f92bba8b001d4363065928088de22f25a3a48", + "reference": "819f92bba8b001d4363065928088de22f25a3a48", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.2", + "php": ">=7.2", "phpunit/php-file-iterator": "^2.0.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.0.1", + "phpunit/php-token-stream": "^3.1.3 || ^4.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^4.1", + "sebastian/environment": "^4.2.2", "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" + "theseer/tokenizer": "^1.1.3" }, "require-dev": { - "phpunit/phpunit": "^8.0" + "phpunit/phpunit": "^8.2.2" }, "suggest": { - "ext-xdebug": "^2.6.1" + "ext-xdebug": "^2.7.2" }, "type": "library", "extra": { @@ -489,27 +533,37 @@ "testing", "xunit" ], - "time": "2019-02-26T07:38:26+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-07-26T12:20:09+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "2.0.2", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "050bedf145a257b1ff02746c31894800e5122946" + "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", - "reference": "050bedf145a257b1ff02746c31894800e5122946", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/28af674ff175d0768a5a978e6de83f697d4a7f05", + "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -539,7 +593,17 @@ "filesystem", "iterator" ], - "time": "2018-09-13T20:33:42+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-07-19T06:46:01+00:00" }, { "name": "phpunit/php-text-template", @@ -580,27 +644,31 @@ "keywords": [ "template" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" + }, "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", - "version": "2.1.1", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059" + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b389aebe1b8b0578430bda0c7c95a829608e059", - "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -629,33 +697,43 @@ "keywords": [ "timer" ], - "time": "2019-02-20T10:12:59+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:20:02+00:00" }, { "name": "phpunit/php-token-stream", - "version": "3.0.1", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18" + "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18", - "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/a853a0e183b9db7eed023d7933a858fa1c8d25a3", + "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.1" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -678,46 +756,58 @@ "keywords": [ "tokenizer" ], - "time": "2018-10-30T05:52:18+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", + "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "abandoned": true, + "time": "2020-08-04T08:28:15+00:00" }, { "name": "phpunit/phpunit", - "version": "8.1.5", + "version": "8.5.21", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "01392d4b5878aa617e8d9bc7a529e5febc8fe956" + "reference": "50a58a60b85947b0bee4c8ecfe0f4bbdcf20e984" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/01392d4b5878aa617e8d9bc7a529e5febc8fe956", - "reference": "01392d4b5878aa617e8d9bc7a529e5febc8fe956", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/50a58a60b85947b0bee4c8ecfe0f4bbdcf20e984", + "reference": "50a58a60b85947b0bee4c8ecfe0f4bbdcf20e984", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.1", + "doctrine/instantiator": "^1.3.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.7", - "phar-io/manifest": "^1.0.2", - "phar-io/version": "^2.0", - "php": "^7.2", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^7.0", - "phpunit/php-file-iterator": "^2.0.1", + "myclabs/deep-copy": "^1.10.0", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.2", + "phpspec/prophecy": "^1.10.3", + "phpunit/php-code-coverage": "^7.0.12", + "phpunit/php-file-iterator": "^2.0.4", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", - "sebastian/environment": "^4.1", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^3.0", + "phpunit/php-timer": "^2.1.2", + "sebastian/comparator": "^3.0.2", + "sebastian/diff": "^3.0.2", + "sebastian/environment": "^4.2.3", + "sebastian/exporter": "^3.1.2", + "sebastian/global-state": "^3.0.0", "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0", + "sebastian/resource-operations": "^2.0.1", + "sebastian/type": "^1.1.3", "sebastian/version": "^2.0.1" }, "require-dev": { @@ -726,7 +816,7 @@ "suggest": { "ext-soap": "*", "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" + "phpunit/php-invoker": "^2.0.0" }, "bin": [ "phpunit" @@ -734,7 +824,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "8.1-dev" + "dev-master": "8.5-dev" } }, "autoload": { @@ -760,27 +850,41 @@ "testing", "xunit" ], - "time": "2019-05-14T04:57:31+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.21" + }, + "funding": [ + { + "url": "https://phpunit.de/donate.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-09-25T07:37:20+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -805,29 +909,39 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:15:22+00:00" }, { "name": "sebastian/comparator", - "version": "3.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", "shasum": "" }, "require": { - "php": "^7.1", + "php": ">=7.1", "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -845,6 +959,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -856,10 +974,6 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", @@ -869,24 +983,34 @@ "compare", "equality" ], - "time": "2018-07-12T15:12:46+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:04:30+00:00" }, { "name": "sebastian/diff", - "version": "3.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^7.5 || ^8.0", @@ -908,13 +1032,13 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", @@ -925,24 +1049,34 @@ "unidiff", "unified diff" ], - "time": "2019-02-04T06:01:07+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:59:04+00:00" }, { "name": "sebastian/environment", - "version": "4.2.2", + "version": "4.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^7.5" @@ -978,24 +1112,34 @@ "environment", "hhvm" ], - "time": "2019-05-05T09:05:15+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:53:42+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.0", + "version": "3.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", + "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", "shasum": "" }, "require": { - "php": "^7.0", + "php": ">=7.0", "sebastian/recursion-context": "^3.0" }, "require-dev": { @@ -1018,6 +1162,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -1026,17 +1174,13 @@ "name": "Volker Dusch", "email": "github@wallbash.com" }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Adam Harvey", "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", @@ -1045,24 +1189,34 @@ "export", "exporter" ], - "time": "2017-04-03T13:19:02+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:47:53+00:00" }, { "name": "sebastian/global-state", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" + "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/474fb9edb7ab891665d3bfc6317f42a0a150454b", + "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b", "shasum": "" }, "require": { - "php": "^7.2", + "php": ">=7.2", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, @@ -1099,24 +1253,34 @@ "keywords": [ "global state" ], - "time": "2019-02-01T05:30:01+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:43:24+00:00" }, { "name": "sebastian/object-enumerator", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", "shasum": "" }, "require": { - "php": "^7.0", + "php": ">=7.0", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, @@ -1146,24 +1310,34 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:40:27+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" @@ -1191,24 +1365,34 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:37:18+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" @@ -1229,14 +1413,14 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" @@ -1244,24 +1428,34 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:34:24+00:00" }, { "name": "sebastian/resource-operations", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "type": "library", "extra": { @@ -1286,7 +1480,74 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04T04:07:39+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "abandoned": true, + "time": "2020-11-30T07:30:19+00:00" + }, + { + "name": "sebastian/type", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", + "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:25:11+00:00" }, { "name": "sebastian/version", @@ -1329,24 +1590,28 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/master" + }, "time": "2016-10-03T07:35:21+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.11.0", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "82ebae02209c21113908c229e9883c419720738a" + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", - "reference": "82ebae02209c21113908c229e9883c419720738a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "suggest": { "ext-ctype": "For best performance" @@ -1354,7 +1619,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { @@ -1370,13 +1639,13 @@ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, { "name": "Gert de Pagter", "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for ctype functions", @@ -1387,27 +1656,44 @@ "polyfill", "portable" ], - "time": "2019-02-06T07:57:58+00:00" + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-02-19T12:13:01+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.2", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8" + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/1c42705be2b6c1de5904f8afacef5895cab44bf8", - "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -1427,34 +1713,47 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-04-04T09:56:43+00:00" + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" }, { "name": "webmozart/assert", - "version": "1.4.0", + "version": "1.10.0", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" + "url": "https://github.com/webmozarts/assert.git", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0", + "php": "^7.2 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "^8.5.13" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.10-dev" } }, "autoload": { @@ -1478,7 +1777,11 @@ "check", "validate" ], - "time": "2018-12-25T11:19:39+00:00" + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.10.0" + }, + "time": "2021-03-09T10:59:23+00:00" } ], "aliases": [], @@ -1498,5 +1801,6 @@ }, "platform-dev": { "ext-pdo_sqlite": "*" - } + }, + "plugin-api-version": "2.1.0" } From 29b70e2986df56781b902dc51c71c2d6f85dda23 Mon Sep 17 00:00:00 2001 From: sy-records <52o@qq52o.cn> Date: Sun, 11 Jul 2021 11:17:20 +0800 Subject: [PATCH 254/277] Fix the problem that undefined state status can be used --- www/bug.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/www/bug.php b/www/bug.php index c49f69fc..66405fe9 100644 --- a/www/bug.php +++ b/www/bug.php @@ -436,8 +436,12 @@ } } + global $state_types; + $allowed_state_types = array_filter($state_types, function ($var) { + return $var !== 0; + }); // Require comment for open bugs only - if (empty($_POST['in']['status'])) { + if (empty($_POST['in']['status']) || !isset($allowed_state_types[$_POST['in']['status']])) { $errors[] = "You must provide a status"; } else { if ($_POST['in']['status'] == 'Not a bug' && From 010107def91e0ea48d5c2e55e8074b7e449a4b43 Mon Sep 17 00:00:00 2001 From: peterdd Date: Thu, 21 Oct 2021 14:16:19 +0200 Subject: [PATCH 255/277] clickable labels for radio and checkboxes in bug search form (#96) * usability: make label clickable for radio input * set a right padding for the label so it is obvious the label is for the left side radio select. * clickable labels for asc/desc radio select * right padding also for the asc/desc labels * make the "NOT" checkbox labels clickable --- include/functions.php | 4 ++-- www/css/style.css | 5 +++++ www/search.php | 9 ++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/functions.php b/include/functions.php index a380cee8..bbfd4735 100644 --- a/include/functions.php +++ b/include/functions.php @@ -681,11 +681,11 @@ function show_boolean_options($current) { $options = ['any', 'all', 'raw']; foreach ($options as $val => $type) { - echo '$type \n"; + echo '>'; } } diff --git a/www/css/style.css b/www/css/style.css index 4423814b..cc59017f 100644 --- a/www/css/style.css +++ b/www/css/style.css @@ -593,6 +593,11 @@ td.search-next { width: 33%; } +input[id^="direction"] + label, +input[id^="boolean"] + label { + padding-right: 1em; +} + .Asn { background-color: #bbaaff; } diff --git a/www/search.php b/www/search.php index 27542fc0..e725c56e 100644 --- a/www/search.php +++ b/www/search.php @@ -228,9 +228,8 @@
    - >Ascending -   - >Descending + > + >

    @@ -285,7 +284,7 @@
    @@ -298,7 +297,7 @@ From 4efb5c3428e3633948371353b06f52401202af8f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 21 Oct 2021 14:31:05 +0200 Subject: [PATCH 256/277] Disable submission of "Documentation Problem" bugs Instead point people to the php/doc-en repository. It's still possible to change the bug type to "Documentation Problem" after it has been submitted, e.g. if it turns out a bug is really a documentation issue. --- include/functions.php | 8 ++++++-- www/bug.php | 2 +- www/report.php | 8 ++++++-- www/search.php | 2 +- www/stats.php | 2 +- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/include/functions.php b/include/functions.php index bbfd4735..ac52b81c 100644 --- a/include/functions.php +++ b/include/functions.php @@ -457,11 +457,12 @@ function show_limit_options($limit = 30) * Options include "Bug", "Documentation Problem" and "Feature/Change Request." * * @param string $current bug's current type - * @param bool $all whether or not 'All' should be an option + * @param bool $deprecated whether or not deprecated types should be shown + * @param bool $all whether or not 'All' should be an option * * @retun void */ -function show_type_options($current = 'Bug', $all = false) +function show_type_options($current, $deprecated, $all = false) { global $bug_types; @@ -479,6 +480,9 @@ function show_type_options($current = 'Bug', $all = false) } foreach ($bug_types as $k => $v) { + if ($k === 'Documentation Problem' && !$deprecated) { + continue; + } $selected = strcasecmp($current, $k) ? '' : ' selected="selected"'; $k = htmlentities($k, ENT_QUOTES); echo ""; diff --git a/www/bug.php b/www/bug.php index 66405fe9..433b5d5f 100644 --- a/www/bug.php +++ b/www/bug.php @@ -928,7 +928,7 @@ diff --git a/www/report.php b/www/report.php index ecc4a1e5..8b7e19b3 100644 --- a/www/report.php +++ b/www/report.php @@ -376,7 +376,11 @@ Failure to follow these instructions may result in your bug simply being marked as "not a bug."

    -

    Report PEAR related bugs here

    +

    + Documentation issues should now be reported on the php/doc-en repository. +

    + +

    Report PEAR related bugs here.

    If you feel this bug concerns a security issue, e.g. a buffer overflow, weak encryption, etc, then email @@ -445,7 +449,7 @@

    diff --git a/www/search.php b/www/search.php index e725c56e..38ad03b7 100644 --- a/www/search.php +++ b/www/search.php @@ -250,7 +250,7 @@ - + diff --git a/www/stats.php b/www/stats.php index 0a397366..5a374807 100644 --- a/www/stats.php +++ b/www/stats.php @@ -78,7 +78,7 @@ From d0b0481c1c0465be0db6b2622b98434f98131596 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 4 Dec 2021 17:05:05 +0100 Subject: [PATCH 257/277] Point people to the new issue tracker --- include/functions.php | 2 +- www/report.php | 34 +++++++++------------------------- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/include/functions.php b/include/functions.php index ac52b81c..d7d6d3b1 100644 --- a/include/functions.php +++ b/include/functions.php @@ -480,7 +480,7 @@ function show_type_options($current, $deprecated, $all = false) } foreach ($bug_types as $k => $v) { - if ($k === 'Documentation Problem' && !$deprecated) { + if ($k !== 'Security' && !$deprecated) { continue; } $selected = strcasecmp($current, $k) ? '' : ' selected="selected"'; diff --git a/www/report.php b/www/report.php index 8b7e19b3..9e0c389c 100644 --- a/www/report.php +++ b/www/report.php @@ -362,31 +362,15 @@ response_header('Report - New', $packageAffectedScript); ?> -

    - Before you report a bug, make sure to search for similar bugs using the "Bug List" link. - Also, read the instructions for how to report a bug that someone will want to help fix. -

    - -

    - If you aren't sure that what you're about to report is a bug, you should ask for help using one of the means for support - listed here. -

    - -

    - Failure to follow these instructions may result in your bug simply being marked as "not a bug." -

    - -

    - Documentation issues should now be reported on the php/doc-en repository. -

    - -

    Report PEAR related bugs here.

    - -

    - If you feel this bug concerns a security issue, e.g. a buffer overflow, weak encryption, etc, then email - - - who will assess the situation or use Security as bug type in the form below. +

    + This bug tracker no longer accepts new non-security issues. Instead use one of the following. +

    Date: Sat, 4 Dec 2021 21:07:31 +0100 Subject: [PATCH 258/277] Hide bug submission form by default As people will not read anything no matter how red or bold it is, hide the bug submission form by default. We don't want people to submit their mundane bugs as security issues just because that's the only option. --- www/report.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/www/report.php b/www/report.php index 9e0c389c..2eacf66a 100644 --- a/www/report.php +++ b/www/report.php @@ -363,17 +363,20 @@ ?>

    - This bug tracker no longer accepts new non-security issues. Instead use one of the following. -

      + This bug tracker no longer accepts new non-security issues. Instead use one of the following: +

      Date: Fri, 7 Jan 2022 18:33:35 +0100 Subject: [PATCH 259/277] Fix erroneous HTML markup --- templates/pages/search_how_to.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/pages/search_how_to.php b/templates/pages/search_how_to.php index 4bf76fab..38df68f9 100644 --- a/templates/pages/search_how_to.php +++ b/templates/pages/search_how_to.php @@ -90,7 +90,7 @@ Suspended: Tickets which are waiting on some action which is outside the scope of the PHP developers. - +
    • Wont fix: Tickets where PHP developers won't fix an issue (even though it is acknowlegded as such), for reasons to be stated in the closing comment. From 2255979dc8a5c3d74a55c543816ab2c22a024011 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Tue, 15 Feb 2022 12:56:09 +0100 Subject: [PATCH 260/277] Try to prevent further SPAM by them --- include/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/functions.php b/include/functions.php index d7d6d3b1..d9e0dc67 100644 --- a/include/functions.php +++ b/include/functions.php @@ -254,7 +254,7 @@ function is_spam($string) /* Primitive check for SPAMmy user. Add more later. */ function is_spam_user($email) { - if (preg_match("/(rhsoft|reindl|phpbugreports|bugreprtsz|bugreports\d*@gmail)/i", $email)) { + if (preg_match("/(rhsoft|reindl|phpbugreports|bugreprtsz|bugreports\d*@gmail|training365)/i", $email)) { return true; } return false; From d50077c25477efe02fd0647ac81ae89c8c14848e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 5 Mar 2022 18:11:03 +0100 Subject: [PATCH 261/277] Only allow links to php.net and github.com Activity on bugs.php.net is pretty much down to link spam now. Fight it by only allowing php.net and github.com links. --- include/functions.php | 12 +++++++----- www/bug.php | 21 ++++++++++----------- www/report.php | 8 ++++---- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/include/functions.php b/include/functions.php index d9e0dc67..9b37d60b 100644 --- a/include/functions.php +++ b/include/functions.php @@ -195,10 +195,12 @@ function is_spam($string) return false; } - $count = substr_count(strtolower($string), 'http://') - + substr_count(strtolower($string), 'https://'); - if ($count > 5) { - return true; + if (preg_match_all('/https?:\/\/(\S+)/', $string, $matches)) { + foreach ($matches[1] as $match) { + if (strpos($match, 'php.net') === false && strpos($match, 'github.com') === false) { + return "Due to large amounts of spam, only links to php.net and github.com (including subdomains like gist.github.com) are allowed."; + } + } } $keywords = [ @@ -245,7 +247,7 @@ function is_spam($string) ]; if (preg_match('/\b('. implode('|', $keywords) . ')\b/i', $string)) { - return true; + return "Comment contains spam word, consider rewording."; } return false; diff --git a/www/bug.php b/www/bug.php index 433b5d5f..aa90dbed 100644 --- a/www/bug.php +++ b/www/bug.php @@ -19,7 +19,6 @@ $obsoletePatchRepository = $container->get(ObsoletePatchRepository::class); $patchRepository = $container->get(PatchRepository::class); -define('SPAM_REJECT_MESSAGE', 'Your comment looks like SPAM by its content. Please consider rewording.'); $email = null; // Handle preview @@ -224,8 +223,8 @@ } // primitive spam detection - if (is_spam($ncomment)) { - $errors[] = SPAM_REJECT_MESSAGE; + if ($message = is_spam($ncomment)) { + $errors[] = $message; } if (is_spam($_POST['in']['commentemail'])) { $errors[] = "Please do not SPAM our bug system."; @@ -264,8 +263,8 @@ $ncomment = trim($_POST['ncomment']); // primitive spam detection - if (is_spam($ncomment)) { - $errors[] = SPAM_REJECT_MESSAGE; + if ($message = is_spam($ncomment)) { + $errors[] = $message; } $from = $_POST['in']['commentemail']; @@ -317,8 +316,8 @@ } // primitive spam detection - if ($ncomment && is_spam($ncomment)) { - $errors[] = SPAM_REJECT_MESSAGE; + if ($ncomment && $message = is_spam($ncomment)) { + $errors[] = $message; } if (!empty($_POST['in']['email']) && @@ -388,8 +387,8 @@ $from = isset($_POST['in']['commentemail']) ? $_POST['in']['commentemail'] : ''; // primitive spam detection - if (is_spam($ncomment)) { - $errors[] = SPAM_REJECT_MESSAGE; + if ($message = is_spam($ncomment)) { + $errors[] = $message; } if (is_spam_user($from)) { $errors[] = "Please do not SPAM our bug system."; @@ -417,8 +416,8 @@ } // primitive spam detection - if ($ncomment && is_spam($ncomment)) { - $errors[] = SPAM_REJECT_MESSAGE; + if ($ncomment && $message = is_spam($ncomment)) { + $errors[] = $message; } // Just trusted dev can set CVE-ID diff --git a/www/report.php b/www/report.php index 2eacf66a..c42e2876 100644 --- a/www/report.php +++ b/www/report.php @@ -52,10 +52,10 @@ } elseif ($_POST['captcha'] != $_SESSION['answer']) { $errors[] = 'Incorrect Captcha'; } - if (is_spam($_POST['in']['ldesc']) || - is_spam($_POST['in']['expres']) || - is_spam($_POST['in']['repcode'])) { - $errors[] = 'Spam detected'; + if (($message = is_spam($_POST['in']['ldesc'])) || + ($message = is_spam($_POST['in']['expres'])) || + ($message = is_spam($_POST['in']['repcode']))) { + $errors[] = $message; } } From 7d1c27f5fb5698adb5b0df1054696c2484249f51 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 27 Mar 2022 22:12:15 +0200 Subject: [PATCH 262/277] Add ilutov as trusted dev --- include/trusted-devs.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/trusted-devs.php b/include/trusted-devs.php index 4209e5a8..aa45117c 100644 --- a/include/trusted-devs.php +++ b/include/trusted-devs.php @@ -25,6 +25,7 @@ 'mikemike', 'dharman', 'stas', + 'ilutov', ]; // Distro people (security related) @@ -76,7 +77,8 @@ 'cmb', 'danbrown', 'yohgaki', - 'bukka' + 'bukka', + 'ilutov', ]; $security_developers = array_merge($security_developers, $security_distro_people); From 717f16a6d9c1a309a5ea5dad78a7a0ab0ce97429 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 24 Jun 2023 21:19:47 +0200 Subject: [PATCH 263/277] Make spam filter stricter Require that php.net/github.com is in the host portion. --- include/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/functions.php b/include/functions.php index 9b37d60b..2f70bb7b 100644 --- a/include/functions.php +++ b/include/functions.php @@ -197,7 +197,7 @@ function is_spam($string) if (preg_match_all('/https?:\/\/(\S+)/', $string, $matches)) { foreach ($matches[1] as $match) { - if (strpos($match, 'php.net') === false && strpos($match, 'github.com') === false) { + if (!preg_match('/^[^\/]*(php\.net|github\.com)/', $match)) { return "Due to large amounts of spam, only links to php.net and github.com (including subdomains like gist.github.com) are allowed."; } } From bc9db6eacf574128c1944cf54a934f7e55cb43f2 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 9 Jul 2023 09:21:21 +0200 Subject: [PATCH 264/277] Make spam filter stricter again --- include/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/functions.php b/include/functions.php index 2f70bb7b..961ce0d6 100644 --- a/include/functions.php +++ b/include/functions.php @@ -197,7 +197,7 @@ function is_spam($string) if (preg_match_all('/https?:\/\/(\S+)/', $string, $matches)) { foreach ($matches[1] as $match) { - if (!preg_match('/^[^\/]*(php\.net|github\.com)/', $match)) { + if (!preg_match('/^[^\/)]*(php\.net|github\.com)/', $match)) { return "Due to large amounts of spam, only links to php.net and github.com (including subdomains like gist.github.com) are allowed."; } } From 0edb3c21931357404e5a49055f3c7c6dfdde9e68 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 24 Jun 2023 21:52:42 +0200 Subject: [PATCH 265/277] Disable bug reports completely And point security issues to GitHub advisories. --- www/report.php | 559 +------------------------------------------------ 1 file changed, 11 insertions(+), 548 deletions(-) diff --git a/www/report.php b/www/report.php index c42e2876..143132bf 100644 --- a/www/report.php +++ b/www/report.php @@ -1,558 +1,21 @@ get(PackageRepository::class); -$pseudo_pkgs = $packageRepository->findEnabled($_GET['project'] ?? ''); - -// Authenticate -bugs_authenticate($user, $pw, $logged_in, $user_flags); - -$versionsClient = new Client(); -$cacheDir = (defined('DEVBOX') && true === DEVBOX) ? __DIR__.'/../var/cache' : '/tmp'; -$cache = new Cache($cacheDir); -$versionsGenerator = new Generator($versionsClient, $cache); -$versions = $versionsGenerator->getVersions(); - -// captcha is not necessary if the user is logged in -if (!$logged_in) { - $captcha = $container->get(Captcha::class); -} - -$packageAffectedScript = << -SCRIPT; - -// Handle input -if (isset($_POST['in'])) { - - $errors = incoming_details_are_valid($_POST['in'], 1, $logged_in); - - // Check if session answer is set, then compare it with the post captcha value. - // If it's not the same, then it's an incorrect password. - if (!$logged_in) { - if (!isset($_SESSION['answer'])) { - $errors[] = 'Please enable cookies so the Captcha system can work'; - } elseif ($_POST['captcha'] != $_SESSION['answer']) { - $errors[] = 'Incorrect Captcha'; - } - if (($message = is_spam($_POST['in']['ldesc'])) || - ($message = is_spam($_POST['in']['expres'])) || - ($message = is_spam($_POST['in']['repcode']))) { - $errors[] = $message; - } - } - - // Set auto-generated password when not supplied or logged in - if ($logged_in || $_POST['in']['passwd'] == '') { - $_POST['in']['passwd'] = uniqid(); - } - - // try to verify the user - $_POST['in']['email'] = $auth_user->email; - - $package_name = $_POST['in']['package_name']; - - if (!$errors) { - // When user submits a report, do a search and display the results before allowing them to continue. - if (!isset($_POST['preview']) && empty($_POST['in']['did_luser_search'])) { - - $_POST['in']['did_luser_search'] = 1; - - $where_clause = "WHERE package_name != 'Feature/Change Request'"; - - if (!($user_flags & BUGS_SECURITY_DEV)) { - $where_clause .= " AND private = 'N' "; - } - - // search for a match using keywords from the subject - list($sql_search, $ignored) = format_search_string($_POST['in']['sdesc']); - - $where_clause .= $sql_search; - - $query = "SELECT * from bugdb $where_clause LIMIT 5"; - - $possible_duplicates = $dbh->prepare($query)->execute()->fetchAll(); - - if (!$possible_duplicates) { - $ok_to_submit_report = true; - } else { - response_header("Report - Confirm", $packageAffectedScript); - if (count($_FILES)) { - echo '

      WARNING: YOU MUST RE-UPLOAD YOUR PATCH, OR IT WILL BE IGNORED

      '; - } -?> -

      - Are you sure that you searched before you submitted your bug report? We - found the following bugs that seem to be similar to yours; please check - them before submitting the report as they might contain the solution you - are looking for. -

      - -

      - If you're sure that your report is a genuine bug that has not been reported - before, you can scroll down and click the "Send Bug Report" button again to - really enter the details into our database. -

      - -
      -
    Return bugs with operating system - > NOT + >
    Return bugs reported with CVE-ID - > NOT + >
    Bug Type:
    Bug Type:
    Project Bug Type:
    - - - - -prepare(" - SELECT comment - FROM bugdb_comments - WHERE bug = ? - ORDER BY id DESC - LIMIT 1 - ")->execute([$row['id']])->fetch(\PDO::FETCH_NUM)[0]; - - $summary = $row['ldesc']; - if (strlen($summary) > 256) { - $summary = substr(trim($summary), 0, 256) . ' ...'; - } - - $bug_url = "bug.php?id={$row['id']}"; - - $sdesc = htmlspecialchars($row['sdesc']); - $summary = htmlspecialchars($summary); - $resolution = htmlspecialchars($resolution); - - echo <<< OUTPUT - - - - - - - -OUTPUT; - } - - echo " -
    DescriptionPossible Solution
    {$row['package_name']} : Bug #{$row['id']}: {$sdesc}
    {$summary}
    {$resolution}
    -
    - "; - } - } else { - // We displayed the luser search and they said it really was not already submitted, so let's allow them to submit. - $ok_to_submit_report = true; - } - - if (isset($_POST['edit_after_preview'])) { - $ok_to_submit_report = false; - response_header("Report - New", $packageAffectedScript); - } - - if ($ok_to_submit_report) { - $_POST['in']['reporter_name'] = $auth_user->name; - $_POST['in']['handle'] = $auth_user->handle; - - // Put all text areas together. - $fdesc = "Description:\n------------\n" . $_POST['in']['ldesc'] . "\n\n"; - if (!empty($_POST['in']['repcode'])) { - $fdesc .= "Test script:\n---------------\n"; - $fdesc .= $_POST['in']['repcode'] . "\n\n"; - } - if (!empty($_POST['in']['expres']) || $_POST['in']['expres'] === '0') { - $fdesc .= "Expected result:\n----------------\n"; - $fdesc .= $_POST['in']['expres'] . "\n\n"; - } - if (!empty($_POST['in']['actres']) || $_POST['in']['actres'] === '0') { - $fdesc .= "Actual result:\n--------------\n"; - $fdesc .= $_POST['in']['actres'] . "\n"; - } - - // Bug type 'Security' marks automatically the report as private - $_POST['in']['private'] = ($_POST['in']['bug_type'] == 'Security') ? 'Y' : 'N'; - $_POST['in']['block_user_comment'] = 'N'; - - if (isset($_POST['preview'])) { - $_POST['in']['status'] = 'Open'; - $_SESSION['bug_preview'] = $_POST['in']; - $_SESSION['bug_preview']['ldesc_orig'] = $_POST['in']['ldesc']; - $_SESSION['bug_preview']['ldesc'] = $fdesc; - $_SESSION['captcha'] = $_POST['captcha']; - redirect('bug.php?id=preview'); - } - - $res = $dbh->prepare(' - INSERT INTO bugdb ( - package_name, - bug_type, - email, - sdesc, - ldesc, - php_version, - php_os, - passwd, - reporter_name, - status, - ts1, - private, - visitor_ip - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, "Open", NOW(), ?, INET6_ATON(?)) - ')->execute([ - $package_name, - $_POST['in']['bug_type'], - $_POST['in']['email'], - $_POST['in']['sdesc'], - $fdesc, - $_POST['in']['php_version'], - $_POST['in']['php_os'], - bugs_get_hash($_POST['in']['passwd']), - $_POST['in']['reporter_name'], - $_POST['in']['private'], - $_SERVER['REMOTE_ADDR'] - ] - ); - - $cid = $dbh->lastInsertId(); - - $redirectToPatchAdd = false; - if (!empty($_POST['in']['patchname']) && $_POST['in']['patchname']) { - $tracker = $container->get(PatchTracker::class); - - try { - $developer = !empty($_POST['in']['handle']) ? $_POST['in']['handle'] : $_POST['in']['email']; - $patchrevision = $tracker->attach($cid, 'patchfile', $_POST['in']['patchname'], $developer, []); - } catch (\Exception $e) { - $redirectToPatchAdd = true; - } - } - - if (empty($_POST['in']['handle'])) { - $mailfrom = spam_protect($_POST['in']['email'], 'text'); - } else { - $mailfrom = $_POST['in']['handle']; - } - - $report = <<< REPORT -From: {$mailfrom} -Operating system: {$_POST['in']['php_os']} -PHP version: {$_POST['in']['php_version']} -Package: {$package_name} -Bug Type: {$_POST['in']['bug_type']} -Bug description: -REPORT; - - $ascii_report = "{$report}{$_POST['in']['sdesc']}\n\n" . wordwrap($fdesc, 72); - $ascii_report.= "\n-- \nEdit bug report at "; - $ascii_report.= "{$site_method}://{$site_url}{$basedir}/bug.php?id=$cid&edit="; - - list($mailto, $mailfrom, $bcc, $params) = get_package_mail($package_name, false, $_POST['in']['bug_type']); - - $protected_email = '"' . spam_protect($_POST['in']['email'], 'text') . '"' . "<{$mailfrom}>"; - - $extra_headers = "From: {$protected_email}\n"; - $extra_headers.= "X-PHP-BugTracker: {$siteBig}bug\n"; - $extra_headers.= "X-PHP-Bug: {$cid}\n"; - $extra_headers.= "X-PHP-Type: {$_POST['in']['bug_type']}\n"; - $extra_headers.= "X-PHP-Version: {$_POST['in']['php_version']}\n"; - $extra_headers.= "X-PHP-Category: {$package_name}\n"; - $extra_headers.= "X-PHP-OS: {$_POST['in']['php_os']}\n"; - $extra_headers.= "X-PHP-Status: Open\n"; - $extra_headers.= "Message-ID: "; - - if (isset($bug_types[$_POST['in']['bug_type']])) { - $type = $bug_types[$_POST['in']['bug_type']]; - } else { - $type = 'unknown'; - } - - // provide shortcut URLS for "quick bug fixes" - $reasonRepository = $container->get(ReasonRepository::class); - list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($_GET['project'] ?? ''); - - $dev_extra = ''; - $maxkeysize = 0; - foreach ($RESOLVE_REASONS as $v) { - if (!$v['webonly']) { - $actkeysize = strlen($v['title']) + 1; - $maxkeysize = (($maxkeysize < $actkeysize) ? $actkeysize : $maxkeysize); - } - } - foreach ($RESOLVE_REASONS as $k => $v) { - if (!$v['webonly']) { - $dev_extra .= str_pad("{$v['title']}:", $maxkeysize) . " {$site_method}://{$site_url}/fix.php?id={$cid}&r={$k}\n"; - } - } - - // mail to reporter - bugs_mail( - $_POST['in']['email'], - "$type #$cid: {$_POST['in']['sdesc']}", - "{$ascii_report}2\n", - "From: $siteBig Bug Database <$mailfrom>\n" . - "X-PHP-Bug: $cid\n" . - "X-PHP-Site: {$siteBig}\n" . - "Message-ID: " - ); - - // mail to package mailing list - bugs_mail( - $mailto, - "[$siteBig-BUG] $type #$cid [NEW]: {$_POST['in']['sdesc']}", - $ascii_report . "1\n-- \n{$dev_extra}", - $extra_headers, - $params - ); - - if ($redirectToPatchAdd) { - $patchname = urlencode($_POST['in']['patchname']); - $patchemail= urlencode($_POST['in']['email']); - redirect("patch-add.php?bug_id={$cid}&patchname={$patchname}&email={$patchemail}"); - } - redirect("bug.php?id={$cid}&thanks=4"); - } - } else { - // had errors... - response_header('Report - Problems', $packageAffectedScript); - } -} // end of if input - -$package = !empty($_REQUEST['package']) ? $_REQUEST['package'] : (!empty($package_name) ? $package_name : (isset($_POST['in']) && $_POST['in'] && isset($_POST['in']['package_name']) ? $_POST['in']['package_name'] : '')); - -if (!is_string($package)) { - response_header('Report - Problems', $packageAffectedScript); - $errors[] = 'Invalid package name passed. Please fix it and try again.'; - display_bug_error($errors); - response_footer(); - exit; -} - -if (!isset($_POST['in'])) { - - $_POST['in'] = [ - 'package_name' => isset($_GET['package_name']) ? clean($_GET['package_name']) : '', - 'bug_type' => isset($_GET['bug_type']) ? clean($_GET['bug_type']) : '', - 'email' => '', - 'sdesc' => '', - 'ldesc' => isset($_GET['manpage']) ? clean("\n---\nFrom manual page: https://php.net/" . ltrim($_GET['manpage'], '/') . "\n---\n") : '', - 'repcode' => '', - 'expres' => '', - 'actres' => '', - 'php_version' => '', - 'php_os' => '', - 'passwd' => '', - ]; - - - response_header('Report - New', $packageAffectedScript); -?> - -

    - This bug tracker no longer accepts new non-security issues. Instead use one of the following: -

    -

    - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -getAnswer(); - - if (!empty($_POST['captcha']) && empty($ok_to_submit_report)) { - $captcha_label = 'Solve this new problem:'; - } else { - $captcha_label = 'Solve the problem:'; - } -?> - - - - - +

    + This bug tracker no longer accepts new issues. Instead use one of the following: +

    +

    - - - - -
    Your handle: - handle; ?> - -
    Your email address:
    MUST BE VALID
    - -
    Password: -
    - You must enter any password here, which will be stored for this bug report.
    - This password allows you to come back and modify your submitted bug report at a later date. - [Lost a bug password?] -
    PHP version: - -
    Package affected: - -
    Bug Type: - -
    Operating system: - -
    Summary: - -
    Note: - Please supply any information that may be helpful in fixing the bug: -
      -
    • The version number of the package or files you are using.
    • -
    • A short script that reproduces the problem.
    • -
    • The list of modules you compiled PHP with (your configure line).
    • -
    • Any other information unique or specific to your setup.
    • -
    • Any changes made in your php.ini compared to php.ini-dist or php.ini-recommended (not your whole php.ini!)
    • -
    • A gdb backtrace.
    • -
    -
    - Description: -

    - Put short code samples in the "Test script" section below - and upload patches below. -

    -
    - -
    - Test script: -

    - A short test script you wrote that demonstrates the bug. - Please do not post more than 20 lines of code. - If the code is longer than 20 lines, provide a URL to the source - code that will reproduce the bug. -

    -
    - -
    - Expected result: -

    - Skip if irrelevant. - What do you expect to happen or see when you run the test script above? -

    -
    - -
    - Actual result: -

    - Skip if irrelevant. - This could be a backtrace for example. - Try to keep it as short as possible without leaving anything relevant out. -

    -
    - -

    getQuestion()); ?>
    Submit: - - -
    -
    Date: Mon, 11 Sep 2023 13:32:24 +0200 Subject: [PATCH 266/277] debug builds are not required for GDB (#117) --- templates/pages/bugs_generating_backtrace.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/pages/bugs_generating_backtrace.php b/templates/pages/bugs_generating_backtrace.php index b99f3831..4627b9fe 100644 --- a/templates/pages/bugs_generating_backtrace.php +++ b/templates/pages/bugs_generating_backtrace.php @@ -19,8 +19,8 @@ 1.3).

    Important!

    -To get a backtrace with correct information you must have PHP configured with ---enable-debug! +To get a backtrace with correct information you must have +a non stripped PHP binary!

    If you don't have a core file yet:

    From 8a36df538c11961c7601c46b369a85690f97e0ce Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Sun, 17 Sep 2023 14:12:32 +0100 Subject: [PATCH 267/277] Remove visitor_ip from columns. The value we collected was wrong, and new MariaDB bails out on it --- include/functions.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/functions.php b/include/functions.php index 961ce0d6..cc072839 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1450,10 +1450,10 @@ function bugs_add_comment($bug_id, $from, $from_name, $comment, $type = 'comment global $dbh; return $dbh->prepare(" - INSERT INTO bugdb_comments (bug, email, reporter_name, comment, comment_type, ts, visitor_ip) - VALUES (?, ?, ?, ?, ?, NOW(), INET6_ATON(?)) + INSERT INTO bugdb_comments (bug, email, reporter_name, comment, comment_type, ts) + VALUES (?, ?, ?, ?, ?, NOW()) ")->execute([ - $bug_id, $from, $from_name, $comment, $type, (!empty($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:'127.0.0.1') + $bug_id, $from, $from_name, $comment, $type ]); } From 9ed00f752fb1f82a93ed08a13f45219e5723c0d0 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Mon, 20 Nov 2023 12:43:13 +0000 Subject: [PATCH 268/277] Fixed line endings for setting headers During the recent server upgrades, it seems that the MTA does no longer 'convert' this. In fact, this has always been wrong. --- include/functions.php | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/include/functions.php b/include/functions.php index cc072839..f03ddb04 100644 --- a/include/functions.php +++ b/include/functions.php @@ -968,10 +968,10 @@ function mail_bug_updates($bug, $in, $from, $ncomment, $edit = 1, $id = false) $bug['email'], "{$subj}: {$sdesc}", $user_text, - "From: {$siteBig} Bug Database <{$mailfrom}>\n" . - "Bcc: {$bcc}\n" . - "X-PHP-Bug: {$bug['id']}\n" . - "X-PHP-Site: {$siteBig}\n" . + "From: {$siteBig} Bug Database <{$mailfrom}>\r\n" . + "Bcc: {$bcc}\r\n" . + "X-PHP-Bug: {$bug['id']}\r\n" . + "X-PHP-Site: {$siteBig}\r\n" . "In-Reply-To: " ); @@ -988,15 +988,15 @@ function mail_bug_updates($bug, $in, $from, $ncomment, $edit = 1, $id = false) $mailto, "{$subj}: {$sdesc}", $dev_text, - "From: {$from}\n". - "X-PHP-Bug: {$bug['id']}\n" . - "X-PHP-Site: {$siteBig}\n" . - "X-PHP-Type: {$tmp['bug_type']}\n" . - "X-PHP-Version: {$tmp['php_version']}\n" . - "X-PHP-Category: {$tmp['package_name']}\n" . - "X-PHP-OS: {$tmp['php_os']}\n" . - "X-PHP-Status: {$tmp['new_status']}\n" . - "X-PHP-Old-Status: {$tmp['old_status']}\n" . + "From: {$from}\r\n". + "X-PHP-Bug: {$bug['id']}\r\n" . + "X-PHP-Site: {$siteBig}\r\n" . + "X-PHP-Type: {$tmp['bug_type']}\r\n" . + "X-PHP-Version: {$tmp['php_version']}\r\n" . + "X-PHP-Category: {$tmp['package_name']}\r\n" . + "X-PHP-OS: {$tmp['php_os']}\r\n" . + "X-PHP-Status: {$tmp['new_status']}\r\n" . + "X-PHP-Old-Status: {$tmp['old_status']}\r\n" . "In-Reply-To: ", $params ); @@ -1016,8 +1016,8 @@ function mail_bug_updates($bug, $in, $from, $ncomment, $edit = 1, $id = false) $email, $bug_types[$bug['bug_type']] . ' #' . $bug['id'] . ' ' . txfield('sdesc', $bug, $in), "{$in['assign']} you have just been assigned to this bug by {$from}\n\n{$dev_text}", - "From: {$from}\n" . - "X-PHP-Bug: {$bug['id']}\n" . + "From: {$from}\r\n" . + "X-PHP-Bug: {$bug['id']}\r\n" . "In-Reply-To: " ); } @@ -1393,8 +1393,8 @@ function unsubscribe_hash($bug_id, $email) $email, "[$siteBig-BUG-unsubscribe] #{$bug_id}", $user_text, - "From: {$siteBig} Bug Database <{$bugEmail}>\n". - "X-PHP-Bug: {$bug_id}\n". + "From: {$siteBig} Bug Database <{$bugEmail}>\r\n". + "X-PHP-Bug: {$bug_id}\r\n". "In-Reply-To: " ); } From 7e33404735c2fa421483d11f68b8aa87461e531c Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 20 Mar 2024 11:29:24 +0000 Subject: [PATCH 269/277] Add Niels to trusted devs (#119) --- include/trusted-devs.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/trusted-devs.php b/include/trusted-devs.php index aa45117c..80bf54a5 100644 --- a/include/trusted-devs.php +++ b/include/trusted-devs.php @@ -26,6 +26,7 @@ 'dharman', 'stas', 'ilutov', + 'nielsdos', ]; // Distro people (security related) @@ -79,6 +80,7 @@ 'yohgaki', 'bukka', 'ilutov', + 'nielsdos', ]; $security_developers = array_merge($security_developers, $security_distro_people); From 2d6fbb56f42a03147d9eaf62869680334985eee1 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Wed, 31 Jul 2024 17:41:33 +0100 Subject: [PATCH 270/277] Update composer and dependencies for PHP 8.2 --- composer.json | 2 +- composer.lock | 701 +++++++++++++------------------------------------- 2 files changed, 174 insertions(+), 529 deletions(-) diff --git a/composer.json b/composer.json index 6bee75dd..a9a9ae51 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "rss": "https://bugs.php.net/rss" }, "require": { - "php": "^7.3", + "php": "^8.2", "ext-fileinfo": "*", "ext-json": "*", "ext-mbstring": "*", diff --git a/composer.lock b/composer.lock index bcfb87d8..8ad77d94 100644 --- a/composer.lock +++ b/composer.lock @@ -4,34 +4,35 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "994d11b989a10210dc468c7046539905", + "content-hash": "9ffaca7d3269343f2ea635047d22621e", "packages": [], "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^8.0", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { @@ -58,7 +59,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { @@ -74,41 +75,43 @@ "type": "tidelift" } ], - "time": "2020-11-10T18:47:58+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.10.2", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, - "replace": { - "myclabs/deep-copy": "self.version" + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, "files": [ "src/DeepCopy/deep_copy.php" - ] + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -124,7 +127,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" }, "funding": [ { @@ -132,24 +135,25 @@ "type": "tidelift" } ], - "time": "2020-11-13T09:40:50+00:00" + "time": "2024-06-12T14:39:25+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", @@ -190,22 +194,28 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", - "version": "3.1.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "bae7c545bef187884426f042434e561ab1ddb182" + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", - "reference": "bae7c545bef187884426f042434e561ab1ddb182", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { @@ -241,249 +251,22 @@ "description": "Library for handling version information and constraints", "support": { "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.1.0" - }, - "time": "2021-02-23T14:00:09+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org/", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.5.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", - "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1" + "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "time": "2021-10-02T14:08:47+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "1.14.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.2", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com/" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.14.0" - }, - "time": "2021-09-10T09:02:12+00:00" + "time": "2022-02-21T01:04:05+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "7.0.15", + "version": "7.0.17", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "819f92bba8b001d4363065928088de22f25a3a48" + "reference": "40a4ed114a4aea5afd6df8d0f0c9cd3033097f66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/819f92bba8b001d4363065928088de22f25a3a48", - "reference": "819f92bba8b001d4363065928088de22f25a3a48", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/40a4ed114a4aea5afd6df8d0f0c9cd3033097f66", + "reference": "40a4ed114a4aea5afd6df8d0f0c9cd3033097f66", "shasum": "" }, "require": { @@ -535,7 +318,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.17" }, "funding": [ { @@ -543,20 +326,20 @@ "type": "github" } ], - "time": "2021-07-26T12:20:09+00:00" + "time": "2024-03-02T06:09:37+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "2.0.4", + "version": "2.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05" + "reference": "69deeb8664f611f156a924154985fbd4911eb36b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/28af674ff175d0768a5a978e6de83f697d4a7f05", - "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/69deeb8664f611f156a924154985fbd4911eb36b", + "reference": "69deeb8664f611f156a924154985fbd4911eb36b", "shasum": "" }, "require": { @@ -595,7 +378,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.4" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.6" }, "funding": [ { @@ -603,7 +386,7 @@ "type": "github" } ], - "time": "2021-07-19T06:46:01+00:00" + "time": "2024-03-01T13:39:50+00:00" }, { "name": "phpunit/php-text-template", @@ -652,16 +435,16 @@ }, { "name": "phpunit/php-timer", - "version": "2.1.3", + "version": "2.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" + "reference": "a691211e94ff39a34811abd521c31bd5b305b0bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/a691211e94ff39a34811abd521c31bd5b305b0bb", + "reference": "a691211e94ff39a34811abd521c31bd5b305b0bb", "shasum": "" }, "require": { @@ -699,7 +482,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" + "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.4" }, "funding": [ { @@ -707,7 +490,7 @@ "type": "github" } ], - "time": "2020-11-30T08:20:02+00:00" + "time": "2024-03-01T13:42:41+00:00" }, { "name": "phpunit/php-token-stream", @@ -771,52 +554,48 @@ }, { "name": "phpunit/phpunit", - "version": "8.5.21", + "version": "8.5.39", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "50a58a60b85947b0bee4c8ecfe0f4bbdcf20e984" + "reference": "172ba97bcf97ae6ef86ca256adf77aece8a143fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/50a58a60b85947b0bee4c8ecfe0f4bbdcf20e984", - "reference": "50a58a60b85947b0bee4c8ecfe0f4bbdcf20e984", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/172ba97bcf97ae6ef86ca256adf77aece8a143fe", + "reference": "172ba97bcf97ae6ef86ca256adf77aece8a143fe", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", + "doctrine/instantiator": "^1.5.0", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.0", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=7.2", - "phpspec/prophecy": "^1.10.3", - "phpunit/php-code-coverage": "^7.0.12", - "phpunit/php-file-iterator": "^2.0.4", + "phpunit/php-code-coverage": "^7.0.17", + "phpunit/php-file-iterator": "^2.0.6", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1.2", - "sebastian/comparator": "^3.0.2", - "sebastian/diff": "^3.0.2", - "sebastian/environment": "^4.2.3", - "sebastian/exporter": "^3.1.2", - "sebastian/global-state": "^3.0.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0.1", - "sebastian/type": "^1.1.3", + "phpunit/php-timer": "^2.1.4", + "sebastian/comparator": "^3.0.5", + "sebastian/diff": "^3.0.6", + "sebastian/environment": "^4.2.5", + "sebastian/exporter": "^3.1.6", + "sebastian/global-state": "^3.0.5", + "sebastian/object-enumerator": "^3.0.5", + "sebastian/resource-operations": "^2.0.3", + "sebastian/type": "^1.1.5", "sebastian/version": "^2.0.1" }, - "require-dev": { - "ext-pdo": "*" - }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0.0" + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage", + "phpunit/php-invoker": "To allow enforcing time limits" }, "bin": [ "phpunit" @@ -852,32 +631,37 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.21" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.39" }, "funding": [ { - "url": "https://phpunit.de/donate.html", + "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "time": "2021-09-25T07:37:20+00:00" + "time": "2024-07-10T11:43:00+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" + "reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54", + "reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54", "shasum": "" }, "require": { @@ -911,7 +695,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.3" }, "funding": [ { @@ -919,20 +703,20 @@ "type": "github" } ], - "time": "2020-11-30T08:15:22+00:00" + "time": "2024-03-01T13:45:45+00:00" }, { "name": "sebastian/comparator", - "version": "3.0.3", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" + "reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dc7ceb4a24aede938c7af2a9ed1de09609ca770", + "reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770", "shasum": "" }, "require": { @@ -985,7 +769,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.5" }, "funding": [ { @@ -993,20 +777,20 @@ "type": "github" } ], - "time": "2020-11-30T08:04:30+00:00" + "time": "2022-09-14T12:31:48+00:00" }, { "name": "sebastian/diff", - "version": "3.0.3", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" + "reference": "98ff311ca519c3aa73ccd3de053bdb377171d7b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/98ff311ca519c3aa73ccd3de053bdb377171d7b6", + "reference": "98ff311ca519c3aa73ccd3de053bdb377171d7b6", "shasum": "" }, "require": { @@ -1051,7 +835,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/diff/tree/3.0.6" }, "funding": [ { @@ -1059,20 +843,20 @@ "type": "github" } ], - "time": "2020-11-30T07:59:04+00:00" + "time": "2024-03-02T06:16:36+00:00" }, { "name": "sebastian/environment", - "version": "4.2.4", + "version": "4.2.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" + "reference": "56932f6049a0482853056ffd617c91ffcc754205" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/56932f6049a0482853056ffd617c91ffcc754205", + "reference": "56932f6049a0482853056ffd617c91ffcc754205", "shasum": "" }, "require": { @@ -1114,7 +898,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" + "source": "https://github.com/sebastianbergmann/environment/tree/4.2.5" }, "funding": [ { @@ -1122,29 +906,29 @@ "type": "github" } ], - "time": "2020-11-30T07:53:42+00:00" + "time": "2024-03-01T13:49:59+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.3", + "version": "3.1.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" + "reference": "1939bc8fd1d39adcfa88c5b35335910869214c56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/1939bc8fd1d39adcfa88c5b35335910869214c56", + "reference": "1939bc8fd1d39adcfa88c5b35335910869214c56", "shasum": "" }, "require": { - "php": ">=7.0", + "php": ">=7.2", "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -1191,7 +975,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3" + "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.6" }, "funding": [ { @@ -1199,20 +983,20 @@ "type": "github" } ], - "time": "2020-11-30T07:47:53+00:00" + "time": "2024-03-02T06:21:38+00:00" }, { "name": "sebastian/global-state", - "version": "3.0.1", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b" + "reference": "91c7c47047a971f02de57ed6f040087ef110c5d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/474fb9edb7ab891665d3bfc6317f42a0a150454b", - "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/91c7c47047a971f02de57ed6f040087ef110c5d9", + "reference": "91c7c47047a971f02de57ed6f040087ef110c5d9", "shasum": "" }, "require": { @@ -1255,7 +1039,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.5" }, "funding": [ { @@ -1263,20 +1047,20 @@ "type": "github" } ], - "time": "2020-11-30T07:43:24+00:00" + "time": "2024-03-02T06:13:16+00:00" }, { "name": "sebastian/object-enumerator", - "version": "3.0.4", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" + "reference": "ac5b293dba925751b808e02923399fb44ff0d541" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/ac5b293dba925751b808e02923399fb44ff0d541", + "reference": "ac5b293dba925751b808e02923399fb44ff0d541", "shasum": "" }, "require": { @@ -1312,7 +1096,7 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.5" }, "funding": [ { @@ -1320,20 +1104,20 @@ "type": "github" } ], - "time": "2020-11-30T07:40:27+00:00" + "time": "2024-03-01T13:54:02+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" + "reference": "1d439c229e61f244ff1f211e5c99737f90c67def" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/1d439c229e61f244ff1f211e5c99737f90c67def", + "reference": "1d439c229e61f244ff1f211e5c99737f90c67def", "shasum": "" }, "require": { @@ -1367,7 +1151,7 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.3" }, "funding": [ { @@ -1375,20 +1159,20 @@ "type": "github" } ], - "time": "2020-11-30T07:37:18+00:00" + "time": "2024-03-01T13:56:04+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" + "reference": "9bfd3c6f1f08c026f542032dfb42813544f7d64c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/9bfd3c6f1f08c026f542032dfb42813544f7d64c", + "reference": "9bfd3c6f1f08c026f542032dfb42813544f7d64c", "shasum": "" }, "require": { @@ -1430,7 +1214,7 @@ "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.2" }, "funding": [ { @@ -1438,20 +1222,20 @@ "type": "github" } ], - "time": "2020-11-30T07:34:24+00:00" + "time": "2024-03-01T14:07:30+00:00" }, { "name": "sebastian/resource-operations", - "version": "2.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" + "reference": "72a7f7674d053d548003b16ff5a106e7e0e06eee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/72a7f7674d053d548003b16ff5a106e7e0e06eee", + "reference": "72a7f7674d053d548003b16ff5a106e7e0e06eee", "shasum": "" }, "require": { @@ -1481,8 +1265,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.3" }, "funding": [ { @@ -1490,21 +1273,20 @@ "type": "github" } ], - "abandoned": true, - "time": "2020-11-30T07:30:19+00:00" + "time": "2024-03-01T13:59:09+00:00" }, { "name": "sebastian/type", - "version": "1.1.4", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" + "reference": "18f071c3a29892b037d35e6b20ddf3ea39b42874" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", - "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/18f071c3a29892b037d35e6b20ddf3ea39b42874", + "reference": "18f071c3a29892b037d35e6b20ddf3ea39b42874", "shasum": "" }, "require": { @@ -1539,7 +1321,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" + "source": "https://github.com/sebastianbergmann/type/tree/1.1.5" }, "funding": [ { @@ -1547,7 +1329,7 @@ "type": "github" } ], - "time": "2020-11-30T07:25:11+00:00" + "time": "2024-03-01T14:04:07+00:00" }, { "name": "sebastian/version", @@ -1596,97 +1378,18 @@ }, "time": "2016-10-03T07:35:21+00:00" }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.23.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", - "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com/", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-02-19T12:13:01+00:00" - }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { @@ -1715,7 +1418,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, "funding": [ { @@ -1723,65 +1426,7 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.10.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", - "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.10.0" - }, - "time": "2021-03-09T10:59:23+00:00" + "time": "2024-03-03T12:36:25+00:00" } ], "aliases": [], @@ -1790,7 +1435,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.3", + "php": "^8.2", "ext-fileinfo": "*", "ext-json": "*", "ext-mbstring": "*", @@ -1802,5 +1447,5 @@ "platform-dev": { "ext-pdo_sqlite": "*" }, - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.3.0" } From 4792d120692846ec0acc5562d3c59a9826cd694a Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Wed, 31 Jul 2024 17:42:19 +0100 Subject: [PATCH 271/277] Fix PHP 8.2/8.3 warnings and deprecations --- include/functions.php | 6 +++--- src/Database/Statement.php | 1 + www/bug.php | 4 ++-- www/search.php | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/functions.php b/include/functions.php index f03ddb04..381dbc57 100644 --- a/include/functions.php +++ b/include/functions.php @@ -334,7 +334,7 @@ function field($n) { return oneof(isset($_POST['in']) ? htmlspecialchars(isset($_POST['in'][$n]) ? $_POST['in'][$n] : '') : null, - htmlspecialchars($GLOBALS['bug'][$n])); + htmlspecialchars($GLOBALS['bug'][$n] ?? '')); } /** @@ -656,7 +656,7 @@ function ($value) { foreach ($bug_groups as $key => $bug_group) { - echo ""; array_unshift($bug_group[2], $key); @@ -1240,7 +1240,7 @@ function get_package_mail($package_name, $bug_id = false, $bug_type = 'Bug') // Security problems *always* go to the sec team $to[] = $secBugEmail; foreach ($security_distro_people as $user) { - $to[] = "${user}@php.net"; + $to[] = "{$user}@php.net"; } $params = '-f bounce-no-user@php.net'; } diff --git a/src/Database/Statement.php b/src/Database/Statement.php index f967ef9f..3a99d754 100644 --- a/src/Database/Statement.php +++ b/src/Database/Statement.php @@ -13,6 +13,7 @@ class Statement extends \PDOStatement * \PDOStatement::execute(), on the other hand, returns boolean. Change it * to return $this and thus allow further method chaining. */ + #[\ReturnTypeWillChange] public function execute($parameters = null): self { parent::execute($parameters); diff --git a/www/bug.php b/www/bug.php index aa90dbed..7e784c33 100644 --- a/www/bug.php +++ b/www/bug.php @@ -686,7 +686,7 @@ ', htmlspecialchars($bug['assign']); ?> (profile) - + @@ -701,7 +701,7 @@ PHP Version: OS: - + diff --git a/www/search.php b/www/search.php index 38ad03b7..91b0488a 100644 --- a/www/search.php +++ b/www/search.php @@ -10,7 +10,7 @@ // Redirect early if a bug id is passed as search string if (isset($_GET['search_for']) && preg_match('/^\d+$/', trim($_GET['search_for']), $search_for_id_array)) { - redirect("bug.php?id=${search_for_id_array[0]}"); + redirect("bug.php?id={$search_for_id_array[0]}"); } // For bug count only, used in places like doc.php.net @@ -162,7 +162,7 @@ echo ' ', format_date(strtotime($row['ts1'])), "\n"; // Last Modified - $ts2 = strtotime($row['ts2']); + $ts2 = strtotime($row['ts2'] ?? date('Y-m-d H:i:s')); echo ' ' , ($ts2 ? format_date($ts2) : 'Not modified') , "\n"; // Package From 31edeaf6f53875824bbd310c917b2c80f463e789 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Thu, 1 Aug 2024 13:58:41 +0100 Subject: [PATCH 272/277] Fixed 'Last Modified' checking correctly --- www/search.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/search.php b/www/search.php index 91b0488a..66910763 100644 --- a/www/search.php +++ b/www/search.php @@ -162,7 +162,7 @@ echo ' ', format_date(strtotime($row['ts1'])), "\n"; // Last Modified - $ts2 = strtotime($row['ts2'] ?? date('Y-m-d H:i:s')); + $ts2 = $row['ts2'] ? strtotime($row['ts2']) : false; echo ' ' , ($ts2 ? format_date($ts2) : 'Not modified') , "\n"; // Package From 4a29f1a2662413699306109ab2901283292748eb Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sat, 20 Jul 2024 13:07:16 +0200 Subject: [PATCH 273/277] GH-114: Lock down bug tracker to developers only All further conversation about bugs is supposed to happen on Github. We still allow developers to edit the bug tracker, so they can clean up. We start by disallowing users to add patches. --- www/bug.php | 4 +++- www/patch-add.php | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/www/bug.php b/www/bug.php index 7e784c33..fe196481 100644 --- a/www/bug.php +++ b/www/bug.php @@ -1109,7 +1109,9 @@
    OUTPUT; } - echo "

    Add a Patch

    "; + if ($logged_in) { + echo "

    Add a Patch

    "; + } $pullRequestRepository = $container->get(PullRequestRepository::class); $pulls = $pullRequestRepository->findAllByBugId($bug_id); diff --git a/www/patch-add.php b/www/patch-add.php index 9429ced1..12e31add 100644 --- a/www/patch-add.php +++ b/www/patch-add.php @@ -16,6 +16,13 @@ // Authenticate bugs_authenticate($user, $pw, $logged_in, $user_flags); +if (!$logged_in) { + response_header('Developers only'); + display_bug_error('Only developers are allowed to add patches'); + response_footer(); + exit; +} + $canpatch = true; /// Input vars From 8794eabf157a30059c3b3a2b5de03fa2a723681a Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sat, 20 Jul 2024 13:45:12 +0200 Subject: [PATCH 274/277] Disallow users to comment --- www/bug.php | 19 +++++++------------ www/fix.php | 3 +-- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/www/bug.php b/www/bug.php index fe196481..407fdd39 100644 --- a/www/bug.php +++ b/www/bug.php @@ -185,7 +185,7 @@ $project = $bug['project']; // Only fetch stuff when it's really needed -if ($edit && $edit < 3) { +if ($edit && $edit < 2) { $packageRepository = $container->get(PackageRepository::class); $pseudo_pkgs = $packageRepository->findEnabled(); } @@ -210,11 +210,10 @@ // Check if session answer is set, then compare it with the post captcha value. // If it's not the same, then it's an incorrect password. if (!$logged_in) { - if (!isset($_SESSION['answer'])) { - $errors[] = 'Please enable cookies so the Captcha system can work'; - } elseif ($_POST['captcha'] != $_SESSION['answer']) { - $errors[] = 'Incorrect Captcha'; - } + response_header('Developers only'); + display_bug_error('Only developers are allowed to comment; if you are the original reporter use the Edit tab'); + response_footer(); + exit; } $ncomment = trim($_POST['ncomment']); @@ -719,7 +718,6 @@ if ($bug_id !== 'PREVIEW') { echo '
    ', "\n", control(0, 'View'), - ($bug['private'] == 'N' ? control(3, 'Add Comment') : ''), control(1, 'Developer'), (!$email || $bug['email'] == $email? control(2, 'Edit') : ''), '
    ', "\n"; @@ -804,9 +802,7 @@ Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
    - If this is not your bug, you can - add a comment by following this link.
    - If this is your bug, but you forgot your password, you can retrieve your password here.
    + If you forgot your password, you can retrieve your password here.
    @@ -831,8 +827,7 @@ ?>
    Welcome! If you don't have a Git account, you can't do anything here.
    - You can add a comment by following this link - or if you reported this bug, you can edit this bug over here. + If you reported this bug, you can edit this bug over here.
    diff --git a/www/fix.php b/www/fix.php index bd935b79..bd62e1f8 100644 --- a/www/fix.php +++ b/www/fix.php @@ -73,8 +73,7 @@
    Welcome! If you don't have a Git account, you can't do anything here.
    - You can add a comment by following this link - or if you reported this bug, you can edit this bug over here. + If you reported this bug, you can edit this bug over here.
    From c73a67a69fb6065a1eb90ad772a36462e88f08ea Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sat, 20 Jul 2024 16:29:04 +0200 Subject: [PATCH 275/277] Disable voting (fixes #112) --- www/bug.php | 2 +- www/vote.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/www/bug.php b/www/bug.php index 407fdd39..f4700b1d 100644 --- a/www/bug.php +++ b/www/bug.php @@ -1288,5 +1288,5 @@ function control($num, $desc) function canvote($thanks, $status) { - return ($thanks != 4 && $thanks != 6 && $status != 'Closed' && $status != 'Not a bug' && $status != 'Duplicate'); + return false; } diff --git a/www/vote.php b/www/vote.php index e32f21d0..517f7079 100644 --- a/www/vote.php +++ b/www/vote.php @@ -3,6 +3,8 @@ use App\Repository\BugRepository; use App\Repository\VoteRepository; +die('Voting on tickets is disabled'); + // Obtain common includes require_once '../include/prepend.php'; From dc639be31d49b39d0042e9b1c09aea93a7068881 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 31 Jul 2024 19:28:02 +0200 Subject: [PATCH 276/277] We still may need the package list --- www/bug.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/bug.php b/www/bug.php index f4700b1d..1c12c4a1 100644 --- a/www/bug.php +++ b/www/bug.php @@ -185,7 +185,7 @@ $project = $bug['project']; // Only fetch stuff when it's really needed -if ($edit && $edit < 2) { +if ($edit && $edit < 3) { $packageRepository = $container->get(PackageRepository::class); $pseudo_pkgs = $packageRepository->findEnabled(); } From c9451f7a9615e59bccc82cdcdc04b028051d2044 Mon Sep 17 00:00:00 2001 From: Jim Winstead Date: Tue, 10 Sep 2024 14:28:23 -0700 Subject: [PATCH 277/277] Only show 'Add a Pull Request' to logged in users --- www/bug.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/bug.php b/www/bug.php index 1c12c4a1..a1d74d4f 100644 --- a/www/bug.php +++ b/www/bug.php @@ -1113,7 +1113,9 @@ echo "

    Pull Requests

    \n"; require "{$ROOT_DIR}/templates/listpulls.php"; - echo "

    Add a Pull Request

    "; + if ($logged_in) { + echo "

    Add a Pull Request

    "; + } } // Display comments