1<?php 2 3use phpweb\I18n\Languages; 4use phpweb\Navigation\NavItem; 5 6$_SERVER['STATIC_ROOT'] = $MYSITE; 7$_SERVER['MYSITE'] = $MYSITE; 8 9// Use class names instead of colors 10ini_set('highlight.comment', 'comment'); 11ini_set('highlight.default', 'default'); 12ini_set('highlight.keyword', 'keyword'); 13ini_set('highlight.string', 'string'); 14ini_set('highlight.html', 'html'); 15 16// Highlight PHP code 17function highlight_php($code, $return = false) 18{ 19 $highlighted = highlight_string($code, true); 20 21 // Use this ugly hack for now to avoid code snippets with bad syntax screwing up the highlighter 22 if (strstr($highlighted, "include/layout.inc</b>")) { 23 $highlighted = '<span class="html">' . nl2br(htmlentities($code, ENT_HTML5), false) . "</span>"; 24 } 25 26 // Fix output to use CSS classes and wrap well 27 $highlighted = '<div class="phpcode">' . strtr( 28 $highlighted, 29 [ 30 ' ' => ' ', 31 "\n" => '', 32 33 '<span style="color: ' => '<span class="', 34 ], 35 ) . '</div>'; 36 37 if ($return) { return $highlighted; } 38 echo $highlighted; 39 return null; 40} 41 42// Same as highlight_php() but does not require '<?php' in $code 43function highlight_php_trimmed($code, $return = false) 44{ 45 $code = "<?php\n" . $code; 46 $highlighted_code = highlight_php($code, true); 47 $highlighted_code = preg_replace("!<\?php(<br />)+!", '', $highlighted_code, 1); 48 49 if ($return) { return $highlighted_code; } 50 echo $highlighted_code; 51 return null; 52} 53 54// Resize the image using the output of make_image() 55function resize_image($img, $width = 1, $height = 1) 56{ 57 // Drop width and height values from image if available 58 $str = preg_replace('!width=\"([0-9]+?)\"!i', '', $img); 59 $str = preg_replace('!height=\"([0-9]+?)\"!i', '', $str); 60 61 // Return image with new width and height added 62 return preg_replace( 63 '!/?>$!', 64 sprintf(' height="%s" width="%s">', $height, $width), 65 $str, 66 ); 67} 68 69// Return an <img> tag for a given image file available on the server 70function make_image($file, $alt = false, $align = false, $extras = false, 71 $dir = '/images', $addsize = false) 72{ 73 // If no / was provided at the start of $dir, add it 74 $webdir = $_SERVER['MYSITE'] . ($dir[0] == '/' ? '' : '/') . $dir; 75 76 // Get width and height values if possible 77 if ($addsize && ($size = @getimagesize($_SERVER['DOCUMENT_ROOT'] . "$dir/$file"))) { 78 $sizeparams = ' ' . trim($size[3]); 79 } else { 80 $sizeparams = ''; 81 } 82 83 // Convert right or left alignment to CSS float, 84 // but leave other alignments intact (for now) 85 if (in_array($align, ["right", "left"], true)) { 86 $align = ' style="float: ' . $align . ';"'; 87 } elseif ($align) { 88 $align = ' align="' . $align . '"'; 89 } else { 90 $align = ''; 91 } 92 93 // Return with image built up 94 return sprintf('<img src="%s/%s" alt="%s"%s%s%s>', 95 $webdir, 96 $file, 97 ($alt ?: ''), 98 $sizeparams, 99 $align, 100 ($extras ? ' ' . $extras : ''), 101 ); 102} 103 104// Print an <img> tag out for a given file 105function print_image($file, $alt = false, $align = false, $extras = false, 106 $dir = '/images'): void 107{ 108 echo make_image($file, $alt, $align, $extras, $dir); 109} 110 111// Shortcut to usual news image printing (right floating 112// image from the news dir with an alt and an URL) 113function news_image($URL, $image, $alt, $print = true) 114{ 115 $str = "<a href=\"$URL\">" . make_image("news/$image", $alt, "right") . "</a>"; 116 if ($print) { 117 echo $str; 118 } 119 return $str; 120} 121 122// Return HTML code for a submit button image 123function make_submit($file, $alt = false, $align = false, $extras = false, 124 $dir = '/images', $border = 0) 125{ 126 // Get an image without size info and convert the 127 // border attribute to use CSS, as border="" is not 128 // supported on <input> elements in [X]HTML 129 $img = make_image($file, $alt, $align, $extras, $dir, false); 130 $img = str_replace( 131 "border=\"$border\"", 132 "style=\"border: {$border}px;\"", 133 $img, 134 ); 135 136 // Return with ready input image 137 return '<input type="image"' . substr($img, 4); 138} 139 140// Return a hiperlink to something within the site 141function make_link(string $url, string $linktext = ''): string 142{ 143 return sprintf("<a href=\"%s\">%s</a>", $url, $linktext ?: $url); 144} 145 146// make_popup_link() 147// return a hyperlink to something, within the site, that pops up a new window 148// 149function make_popup_link($url, $linktext = false, $target = false, $windowprops = "", $extras = false) { 150 return sprintf("<a href=\"%s\" target=\"%s\" onclick=\"window.open('%s','%s','%s');return false;\"%s>%s</a>", 151 htmlspecialchars($url, ENT_QUOTES | ENT_IGNORE), 152 ($target ?: "_new"), 153 htmlspecialchars($url, ENT_QUOTES | ENT_IGNORE), 154 ($target ?: "_new"), 155 $windowprops, 156 ($extras ? ' ' . $extras : ''), 157 ($linktext ?: $url), 158 ); 159} 160 161// Print a link for a downloadable file (including filesize) 162function download_link($file, $title): void 163{ 164 $download_link = "/distributions/" . $file; 165 166 // Print out the download link 167 echo make_link($download_link, $title); 168 169 // We have a full path or a relative to the distributions dir 170 if ($tmp = strrchr($file, "/")) { 171 $local_file = substr($tmp, 1, strlen($tmp)); 172 } else { 173 $local_file = "distributions/$file"; 174 } 175 176 if (@file_exists($local_file . ".asc")) { 177 echo " "; 178 $sig_link = "/distributions/$file.asc"; 179 echo make_link($sig_link, "(sig)"); 180 } 181 182 // Try to get the size of the file 183 $size = @filesize($local_file); 184 185 // Print out size in bytes (if size is 186 // less then 1Kb, or else in Kb) 187 if ($size) { 188 echo ' ['; 189 if ($size < 1024) { 190 echo number_format($size) . 'b'; 191 } else { 192 echo number_format($size / 1024) . 'Kb'; 193 } 194 echo ']'; 195 } 196} 197 198function clean($var) { 199 return htmlspecialchars($var, ENT_QUOTES); 200} 201 202// Clean out the content of one user note for printing to HTML 203function clean_note($text) 204{ 205 // Highlight PHP source 206 $text = highlight_php(trim($text), true); 207 208 // Turn urls into links 209 return preg_replace( 210 '!((mailto:|(https?|ftp|nntp|news)://).*?)(\s|<|\)|"|\\\\|\'|$)!', 211 '<a href="\1" rel="nofollow" target="_blank">\1</a>\4', 212 $text, 213 ); 214} 215 216function display_errors($errors): void 217{ 218 echo '<div class="errors">'; 219 if (count($errors) > 1) { 220 echo "You need to do the following before your submission will be accepted:<ul>"; 221 foreach ($errors as $error) { 222 echo "<li>$error</li>\n"; 223 } 224 echo "</ul>"; 225 } 226 else { 227 echo $errors[0]; 228 } 229 echo '</div>'; 230} 231 232// Displays an event. Used in event submission 233// previews and event information displays 234function display_event($event, $include_date = 1): void 235{ 236 global $COUNTRIES; 237 // Current month (int)($_GET['cm'] ?: 0) 238 global $cm; 239 // Current year (int)($_GET['cy'] ?: 0) 240 global $cy; 241 242 // Weekday names array 243 for ($i = 1; $i <= 7; $i++) { 244 $days[$i] = date('l', mktime(12, 0, 0, 4, $i, 2012)); 245 } 246 247 // Recurring possibilities 248 $re = [ 249 1 => 'First', 250 2 => 'Second', 251 3 => 'Third', 252 4 => 'Fourth', 253 -1 => 'Last', 254 -2 => '2nd Last', 255 -3 => '3rd Last', 256 ]; 257 258 if (!isset($event['start']) && isset($event['sday'])) { 259 $sday = mktime(12,0,0,$event['smonth'],$event['sday'],$event['syear']); 260 } else { 261 $sday = (isset($event['start']) && !empty($event['start'])) ? strtotime($event['start']) : 0; 262 } 263 264 if (!isset($event['end']) && isset($event['eday'])) { 265 $eday = mktime(12,0,0,$event['emonth'],$event['eday'],$event['eyear']); 266 } else { 267 $eday = (isset($event['end']) && !empty($event['end'])) ? strtotime($event['end']) : 0; 268 } 269?> 270<table border="0" cellspacing="0" cellpadding="3" width="100%" class="vevent"> 271 <tr bgcolor="#dddddd"><td> 272<?php 273 274 // Print out date if needed 275 if ($include_date && (isset($event['start']))) { 276 echo "<b>", date("F j, Y", $sday), "</b>\n"; 277 } 278 279 // Print link in case we have one 280 if ($event['url']) { echo '<a href="', htmlentities($event['url'], ENT_QUOTES | ENT_IGNORE, 'UTF-8'),'" class="url">'; } 281 // Print event description 282 echo "<b class='summary'>", stripslashes(htmlentities($event['sdesc'], ENT_QUOTES | ENT_IGNORE, 'UTF-8')), "</b>"; 283 // End link 284 if ($event['url']) { echo "</a>"; } 285 286 // Print extra date info for recurring and multiday events 287 switch ($event['type']) { 288 case 2: 289 case 'multi': 290 $dtend = date("Y-m-d", strtotime("+1 day", $eday)); 291 echo " (<abbr class='dtstart'>", date("Y-m-d",$sday), "</abbr> to <abbr class='dtend' title='$dtend'>", date("Y-m-d",$eday), "</abbr>)"; 292 break; 293 case 3: 294 case 'recur': 295 $days = $re[$event['recur']] . " " . $days[$event['recur_day']]; 296 if (!$cm || $cy) { 297 $cm = date("m"); 298 $cy = date("Y"); 299 } 300 $month = date("M", mktime(0, 0, 0, $cm, 1, $cy)); 301 $dtstart = date("Y-m-d", strtotime($days . ' 0st' . $month . ' ' . $cy)); 302 echo ' (Every <abbr class="dtstart" title="' . $dtstart . '">', $days, "</abbr> of the month)"; 303 break; 304 } 305 306 // Event category 307 if (isset($event['category']) && $event['category']) { 308 $cat = ["unknown", "User Group Event", "Conference", "Training"]; 309 echo ' [' . $cat[$event['category']] . '] '; 310 } 311 312 // Print out country information 313 echo ' (<span class="location">' , $COUNTRIES[$event['country']] , '</span>)'; 314?> 315 </td></tr> 316 <tr bgcolor="#eeeeee" class="description"><td> 317<?php 318 319 // Print long description 320 echo preg_replace("/\r?\n\r?\n/", "<br><br>", trim(htmlentities($event['ldesc'],ENT_QUOTES | ENT_IGNORE, 'UTF-8'))); 321 // If we have an URL, print it out 322 if ($event['url']) { 323 echo '<br><br><b>URL:</b> ', 324 '<a href="', htmlentities($event['url'], ENT_QUOTES | ENT_IGNORE, 'UTF-8'), '">', 325 htmlentities($event['url'], ENT_QUOTES | ENT_IGNORE, 'UTF-8'), '</a>'; 326 } 327?> 328 </td></tr> 329</table> 330<?php 331} 332 333// Print news links for archives 334function news_archive_sidebar(): void 335{ 336 global $SIDEBAR_DATA; 337 $SIDEBAR_DATA = ' 338<h3 class="announcements">Archives by year</h3> 339 340'; 341 for ($i = date("Y"); $i >= 1998; $i--) { 342 $pagename = "archive/$i.php"; 343 $classname = ($pagename == $_SERVER['BASE_PAGE'] ? " active" : ''); 344 $SIDEBAR_DATA .= "<p class='panel{$classname}'><a href=\"/{$pagename}\">{$i}</a></p>\n"; 345 } 346} 347 348// Print news 349function print_news($news, $dog, $max = 5, $onlyyear = null, $return = false) { 350 $retval = []; 351 $count = 0; 352 $news = $news ?: []; // default to empty array (if no news) 353 foreach ($news as $item) { 354 $ok = false; 355 356 // Only print entries in the provided s/dog/cat/ egory 357 // If $dog is null, everything matches 358 foreach ($item["category"] as $category) { 359 if (null === $dog || in_array($category["term"], (array)$dog, true)) { 360 $ok = true; 361 $count++; 362 break; 363 } 364 } 365 if ($count > $max) { 366 break; 367 } 368 if ($ok === false) { 369 continue; 370 } 371 372 $image = ""; 373 if (isset($item["newsImage"])) { 374 $image = news_image($item["newsImage"]["link"], $item["newsImage"]["content"], $item["newsImage"]["title"], false); 375 } 376 377 $id = parse_url($item["id"]); 378 $id = $id["fragment"]; 379 380 // Find the permlink 381 foreach ($item["link"] as $link) { 382 if ($link["rel"] === "via") { 383 $permlink = $link["href"]; 384 break; 385 } 386 } 387 if (!isset($permlink)) { 388 $permlink = "#" . $id; 389 } 390 391 $published = substr($item["published"], 0, 10); 392 $nixtimestamp = strtotime($published); 393 $newsdate = date("d M Y", $nixtimestamp); 394 if ($onlyyear && date("Y", $nixtimestamp) != $onlyyear) { 395 $count--; 396 continue; 397 } 398 399 if ($return) { 400 $retval[] = [ 401 "title" => $item["title"], 402 "id" => $id, 403 "permlink" => $permlink, 404 "date" => $newsdate, 405 ]; 406 continue; 407 } 408 409 echo <<<EOT 410<article class="newsItem"> 411 <header> 412 <div class="newsImage">{$image}</div> 413 <h2 class="newstitle"><a id="{$id}" href="{$permlink}" rel="bookmark" class="bookmark">{$item["title"]}</a></h2> 414 </header> 415 <time class="newsdate" datetime="{$item["published"]}">{$newsdate}</time> 416 <div class="newscontent"> 417 {$item["content"]} 418 </div> 419</article> 420 421EOT; 422 } 423 424 return $retval; 425} 426 427function site_header(string $title = 'Hypertext Preprocessor', array $config = []): void 428{ 429 global $MYSITE, $LANG; 430 431 $meta_image_path = $MYSITE . 'images/meta-image.png'; 432 $meta_description = "PHP is a popular general-purpose scripting language that powers everything from your blog to the most popular websites in the world."; 433 434 $defaults = [ 435 "lang" => $LANG, 436 "current" => "", 437 "meta-navigation" => [], 438 'classes' => '', 439 'layout_span' => 9, 440 "cache" => false, 441 "headsup" => "", 442 'meta_tags' => <<<META 443<meta name="Description" content="{$meta_description}" /> 444 445<meta name="twitter:card" content="summary_large_image" /> 446<meta name="twitter:site" content="@official_php" /> 447<meta name="twitter:title" content="PHP: Hypertext Preprocessor" /> 448<meta name="twitter:description" content="{$meta_description}" /> 449<meta name="twitter:creator" content="@official_php" /> 450<meta name="twitter:image:src" content="{$meta_image_path}" /> 451 452<meta itemprop="name" content="PHP: Hypertext Preprocessor" /> 453<meta itemprop="description" content="$meta_description" /> 454<meta itemprop="image" content="{$meta_image_path}" /> 455 456<meta property="og:image" content="{$meta_image_path}" /> 457<meta property="og:description" content="$meta_description" /> 458 459<link href="https://fosstodon.org/@php" rel="me" /> 460META 461 ]; 462 463 $config = array_merge($defaults, $config); 464 465 $config["headsup"] = get_news_changes(); 466 467 $lang = (new Languages())->convert($config["lang"]); 468 $curr = $config["current"]; 469 $classes = $config['classes']; 470 471 if (isset($_COOKIE["MD"]) || isset($_GET["MD"])) { 472 $classes .= "markdown-content"; 473 $config["css_overwrite"] = ["/styles/i-love-markdown.css"]; 474 } 475 476 // shorturl; http://wiki.snaplog.com/short_url 477 if (isset($_SERVER['BASE_PAGE']) && $shortname = get_shortname($_SERVER["BASE_PAGE"])) { 478 $shorturl = "https://www.php.net/" . $shortname; 479 } 480 481 require __DIR__ . "/header.inc"; 482} 483function site_footer(array $config = []): void 484{ 485 require __DIR__ . "/footer.inc"; 486} 487 488function get_nav_items(): array { 489 return [ 490 new NavItem( 491 name: 'Downloads', 492 href: '/downloads.php', 493 id: 'downloads', 494 ), 495 new NavItem( 496 name: 'Documentation', 497 href: '/docs.php', 498 id: 'docs', 499 ), 500 new NavItem( 501 name: 'Get Involved', 502 href: '/get-involved.php', 503 id: 'community', 504 ), 505 new NavItem( 506 name: 'Help', 507 href: '/support.php', 508 id: 'help', 509 ), 510 new NavItem( 511 name: 'PHP 8.4', 512 href: '/releases/8.4/index.php', 513 id: 'php8', 514 image: '/images/php8/logo_php8_4.svg', 515 ) 516 ]; 517} 518 519function get_news_changes() 520{ 521 include __DIR__ . "/pregen-news.inc"; 522 $date = date_create($NEWS_ENTRIES[0]["updated"]); 523 if (isset($_COOKIE["LAST_NEWS"]) && $_COOKIE["LAST_NEWS"] >= $date->getTimestamp()) { 524 return false; 525 } 526 527 /* It is a bug when this happens.. but I don't know where it is coming from */ 528 if (!isset($_SERVER["BASE_PAGE"])) { 529 return false; 530 } 531 if ($_SERVER["BASE_PAGE"] == "index.php") { 532 return false; 533 } 534 535 $date->modify("+1 week"); 536 if ($date->getTimestamp() > $_SERVER["REQUEST_TIME"]) { 537 $link = preg_replace('~^(http://php.net/|https://www.php.net/)~', '/', $NEWS_ENTRIES[0]["link"][0]["href"]); 538 $title = $NEWS_ENTRIES[0]["title"]; 539 return "<a href='{$link}'>{$title}</a>"; 540 } 541 return false; 542} 543 544function doc_toc($lang): void { 545 $file = __DIR__ . "/../manual/$lang/toc/index.inc"; 546 if (!file_exists($file)) { 547 $lang = "en"; // Fallback on english if the translation doesn't exist 548 $file = __DIR__ . "/../manual/en/toc/index.inc"; 549 } 550 require __DIR__ . "/../manual/$lang/toc/index.inc"; 551 552 echo "<dl>\n"; 553 doc_toc_list($lang, $TOC, "getting-started"); 554 doc_toc_list($lang, $TOC, "langref"); 555 echo "</dl>\n"; 556 557 echo "<dl>\n"; 558 doc_toc_list($lang, $TOC, "security"); 559 doc_toc_list($lang, $TOC, "features"); 560 echo "</dl>\n"; 561 562 echo "<dl>\n"; 563 doc_toc_list($lang, $TOC, "funcref"); 564 echo "</dl>\n"; 565 566 echo "<dl>\n"; 567 echo "<dt>Keyboard Shortcuts</dt>"; 568 echo "<dt>?</dt>\n"; 569 echo "<dd>This help</dd>\n"; 570 echo "<dt>j</dt>\n"; 571 echo "<dd>Next menu item</dd>\n"; 572 echo "<dt>k</dt>\n"; 573 echo "<dd>Previous menu item</dd>\n"; 574 echo "<dt>g p</dt>\n"; 575 echo "<dd>Previous man page</dd>\n"; 576 echo "<dt>g n</dt>\n"; 577 echo "<dd>Next man page</dd>\n"; 578 echo "<dt>G</dt>\n"; 579 echo "<dd>Scroll to bottom</dd>\n"; 580 echo "<dt>g g</dt>\n"; 581 echo "<dd>Scroll to top</dd>\n"; 582 echo "<dt>g h</dt>\n"; 583 echo "<dd>Goto homepage</dd>\n"; 584 echo "<dt>g s</dt>\n"; 585 echo "<dd>Goto search<br>(current page)</dd>\n"; 586 echo "<dt>/</dt>\n"; 587 echo "<dd>Focus search box</dd>\n"; 588 echo "</dl>"; 589 590} 591function doc_toc_list($lang, $index, $file): void { 592 include __DIR__ . "/../manual/$lang/toc/$file.inc"; 593 594 doc_toc_title($lang, $index, $file); 595 foreach ($TOC as $entry) { 596 echo "\t<dd><a href='/manual/$lang/{$entry[0]}'>{$entry[1]}</a></dd>\n"; 597 } 598} 599function doc_toc_title($lang, $index, $file, $elm = "dt"): void { 600 foreach ($index as $entry) { 601 if ($entry[0] == "$file.php") { 602 $link = $entry[0]; 603 $title = $entry[1]; 604 break; 605 } 606 } 607 echo "<$elm><a href='/manual/$lang/$link'>$title</a></$elm>\n"; 608} 609