= 1 && is_array( $args[ 0 ] ) ) { $configurable_options = $args[ 0 ]; if( isset( $configurable_options[ 'source_dir_path' ] ) && is_string( $configurable_options[ 'source_dir_path' ] ) && trim( $configurable_options[ 'source_dir_path' ] ) != "" ) $this->source_dir_path = trim( $configurable_options[ 'source_dir_path' ] ); if( isset( $configurable_options[ 'output_dir_path' ] ) && is_string( $configurable_options[ 'output_dir_path' ] ) && trim( $configurable_options[ 'output_dir_path' ] ) != "" ) $this->output_dir_path = trim( $configurable_options[ 'output_dir_path' ] ); if( isset( $configurable_options[ 'items_to_ignore' ] ) && is_array( $configurable_options[ 'items_to_ignore' ] ) && count( $configurable_options[ 'items_to_ignore' ] ) > 0 ) $this->items_to_ignore = $configurable_options[ 'items_to_ignore' ]; if( isset( $configurable_options[ 'items_to_ignore' ] ) && is_string( $configurable_options[ 'items_to_ignore' ] ) && trim( $configurable_options[ 'items_to_ignore' ] ) != "" ) $this->items_to_ignore = array( trim( $configurable_options[ 'items_to_ignore' ] ) ); if( isset( $configurable_options[ 'friendly_urls' ] ) && is_bool( $configurable_options[ 'friendly_urls' ] ) ) $this->friendly_urls = $configurable_options[ 'friendly_urls' ]; if( isset( $configurable_options[ 'friendly_urls' ] ) && is_string( $configurable_options[ 'friendly_urls' ] ) && trim( $configurable_options[ 'friendly_urls' ] ) == "true" ) $this->friendly_urls = true; if( isset( $configurable_options[ 'metadata_delimiter' ] ) && is_string( $configurable_options[ 'metadata_delimiter' ] ) && trim( $configurable_options[ 'metadata_delimiter' ] ) != "" ) $this->metaDataDelimiter = $configurable_options[ 'metadata_delimiter' ]; if( isset( $configurable_options[ 'minify_html' ] ) && is_bool( $configurable_options[ 'minify_html' ] ) ) $this->minify_html = $configurable_options[ 'minify_html' ]; if( isset( $configurable_options[ 'minify_html' ] ) && is_string( $configurable_options[ 'minify_html' ] ) && trim( $configurable_options[ 'minify_html' ] ) == "true" ) $this->minify_html = true; if( isset( $configurable_options[ 'minify_css' ] ) && is_bool( $configurable_options[ 'minify_css' ] ) ) $this->minify_css = $configurable_options[ 'minify_css' ]; if( isset( $configurable_options[ 'minify_css' ] ) && is_string( $configurable_options[ 'minify_css' ] ) && trim( $configurable_options[ 'minify_css' ] ) == "true" ) $this->minify_css = true; if( isset( $configurable_options[ 'minify_js' ] ) && is_bool( $configurable_options[ 'minify_js' ] ) ) $this->minify_js = $configurable_options[ 'minify_js' ]; if( isset( $configurable_options[ 'minify_js' ] ) && is_string( $configurable_options[ 'minify_js' ] ) && trim( $configurable_options[ 'minify_js' ] ) == "true" ) $this->minify_js = true; if( isset( $configurable_options[ 'minify_html_tags_to_preserve' ] ) && is_array( $configurable_options[ 'minify_html_tags_to_preserve' ] ) && count( $configurable_options[ 'minify_html_tags_to_preserve' ] ) > 0 ) $this->minify_html_tags_to_preserve = $configurable_options[ 'minify_html_tags_to_preserve' ]; if( isset( $configurable_options[ 'minify_html_tags_to_preserve' ] ) && is_string( $configurable_options[ 'minify_html_tags_to_preserve' ] ) && trim( $configurable_options[ 'minify_html_tags_to_preserve' ] ) != "" ) $this->minify_html_tags_to_preserve = array( trim( $configurable_options[ 'minify_html_tags_to_preserve' ] ) ); if( isset( $configurable_options[ 'bulk_redirects_filename' ] ) && is_string( $configurable_options[ 'bulk_redirects_filename' ] ) && trim( $configurable_options[ 'bulk_redirects_filename' ] ) != "" ) $this->bulk_redirects_filename = trim( $configurable_options[ 'bulk_redirects_filename' ] ); if( isset( $configurable_options[ 'redirection_template_filename' ] ) && is_string( $configurable_options[ 'redirection_template_filename' ] ) && trim( $configurable_options[ 'redirection_template_filename' ] ) != "" ) $this->redirection_template_filename = trim( $configurable_options[ 'redirection_template_filename' ] ); if( isset( $configurable_options[ 'minify_css_inplace' ] ) && is_bool( $configurable_options[ 'minify_css_inplace' ] ) ) $this->minify_css_inplace = $configurable_options[ 'minify_css_inplace' ]; if( isset( $configurable_options[ 'items_to_passthrough' ] ) && is_array( $configurable_options[ 'items_to_passthrough' ] ) && count( $configurable_options[ 'items_to_passthrough' ] ) > 0 ) $this->items_to_passthrough = $configurable_options[ 'items_to_passthrough' ]; if( isset( $configurable_options[ 'items_to_passthrough' ] ) && is_string( $configurable_options[ 'items_to_passthrough' ] ) && trim( $configurable_options[ 'items_to_passthrough' ] ) != "" ) $this->items_to_passthrough = array( $configurable_options[ 'items_to_passthrough' ] ); if( isset( $configurable_options[ 'test_mode' ] ) && is_bool( $configurable_options[ 'test_mode' ] ) ) $this->test_mode = $configurable_options[ 'test_mode' ]; if( isset( $configurable_options[ 'test_mode_input_dir_path' ] ) && is_string( $configurable_options[ 'test_mode_input_dir_path' ] ) && trim( $configurable_options[ 'test_mode_input_dir_path' ] ) != "" ) $this->test_mode_input_dir_path = $configurable_options[ 'test_mode_input_dir_path' ]; if( isset( $configurable_options[ 'test_mode_expected_dir_path' ] ) && is_string( $configurable_options[ 'test_mode_expected_dir_path' ] ) && trim( $configurable_options[ 'test_mode_expected_dir_path' ] ) != "" ) $this->test_mode_expected_dir_path = $configurable_options[ 'test_mode_expected_dir_path' ]; if( isset( $configurable_options[ 'test_mode_output_dir_path' ] ) && is_string( $configurable_options[ 'test_mode_output_dir_path' ] ) && trim( $configurable_options[ 'test_mode_output_dir_path' ] ) != "" ) $this->test_mode_output_dir_path = $configurable_options[ 'test_mode_output_dir_path' ]; if( isset( $configurable_options[ 'test_mode_output_results_file' ] ) && is_bool( $configurable_options[ 'test_mode_output_results_file' ] ) ) $this->test_mode_output_results_file = $configurable_options[ 'test_mode_output_results_file' ]; if( isset( $configurable_options[ 'test_mode_results_file_path' ] ) && is_string( $configurable_options[ 'test_mode_results_file_path' ] ) && trim( $configurable_options[ 'test_mode_results_file_path' ] ) != "" ) $this->test_mode_results_file_path = $configurable_options[ 'test_mode_results_file_path' ]; if( isset( $configurable_options[ 'generate_standard_redirects_file' ] ) && is_bool( $configurable_options[ 'generate_standard_redirects_file' ] ) ) $this->generate_standard_redirects_file = $configurable_options[ 'generate_standard_redirects_file' ]; if( isset( $configurable_options[ 'generate_htaccess_redirections' ] ) && is_bool( $configurable_options[ 'generate_htaccess_redirections' ] ) ) $this->generate_htaccess_redirections = $configurable_options[ 'generate_htaccess_redirections' ]; } // End Array Method // Arguments Method if( count( $args ) >= 1 && is_string( $args[ 0 ] ) && trim( $args[ 0 ] ) != "" ) $this->source_dir_path = trim( $args[ 0 ] ); if( count( $args ) >= 2 && is_string( $args[ 1 ] ) && trim( $args[ 1 ] ) != "" ) $this->output_dir_path = trim( $args[ 1 ] ); if( count( $args ) >= 3 && is_array( $args[ 2 ] ) && count( $args[ 2 ] ) > 0 ) $this->items_to_ignore = trim( $args[ 2 ] ); if( count( $args ) >= 3 && is_string( $args[ 2 ] ) && trim( $args[ 2 ] ) != "" ) $this->items_to_ignore = array( trim( $args[ 2 ] ) ); if( count( $args ) >= 4 && is_bool( $args[ 3 ] ) ) $this->friendly_urls = $args[ 3 ]; if( count( $args ) >= 4 && is_string( $args[ 3 ] ) && trim( $args[ 3 ] ) == "true" ) $this->friendly_urls = true; if( count( $args ) >= 5 && is_string( $args[ 4 ] ) && trim( $args[ 4 ] ) != "" ) $this->metaDataDelimiter = trim( $args[ 4 ] ); if( count( $args ) >= 6 && is_bool( $args[ 5 ] ) ) $this->minify_html = $args[ 5 ]; if( count( $args ) >= 6 && is_string( $args[ 5 ] ) && trim( $args[ 5 ] ) == "true" ) $this->minify_html = true; if( count( $args ) >= 7 && is_bool( $args[ 6 ] ) ) $this->minify_css = $args[ 6 ]; if( count( $args ) >= 7 && is_string( $args[ 6 ] ) && trim( $args[ 6 ] ) == "true" ) $this->minify_css = true; if( count( $args ) >= 8 && is_bool( $args[ 7 ] ) ) $this->minify_js = $args[ 7 ]; if( count( $args ) >= 8 && is_string( $args[ 7 ] ) && trim( $args[ 7 ] ) == "true" ) $this->minify_js = true; if( count( $args ) >= 9 && is_array( $args[ 8 ] ) && count( $args[ 8 ] ) > 0 ) $this->minify_html_tags_to_preserve = trim( $args[ 8 ] ); if( count( $args ) >= 9 && is_string( $args[ 8 ] ) && trim( $args[ 8 ] ) != "" ) $this->minify_html_tags_to_preserve = array( trim( $args[ 8 ] ) ); if( count( $args ) >= 10 && is_string( $args[ 9 ] ) && trim( $args[ 9 ] ) != "" ) $this->bulk_redirects_filename = trim( $args[ 9 ] ); if( count( $args ) >= 11 && is_string( $args[ 10 ] ) && trim( $args[ 10 ] ) != "" ) $this->redirection_template_filename = trim( $args[ 10 ] ); if( count( $args ) >= 12 && is_bool( $args[ 11 ] ) ) $this->minify_css_inplace = $args[ 11 ]; if( count( $args ) >= 13 && is_array( $args[ 12 ] ) ) $this->items_to_passthrough = $args[ 12 ]; if( count( $args ) >= 14 && is_bool( $args[ 13 ] ) ) $this->generate_standard_redirects_file = $args[ 13 ]; if( count( $args ) >= 15 && is_bool( $args[ 14 ] ) ) $this->generate_htaccess_redirections = $args[ 14 ]; // End Arguments Method // Ensure Special Files are Ignored if( ! in_array( $this->redirection_template_filename, $this->items_to_ignore ) ) $this->items_to_ignore[] = $this->redirection_template_filename; $this->source_dir_path = str_replace( [ "\\", "/" ], DIRECTORY_SEPARATOR, $this->source_dir_path ); $this->output_dir_path = str_replace( [ "\\", "/" ], DIRECTORY_SEPARATOR, $this->output_dir_path ); if( $this->test_mode ) { $this->test_mode_input_dir_path = str_replace( [ "\\", "/" ], DIRECTORY_SEPARATOR, $this->test_mode_input_dir_path ); $this->test_mode_expected_dir_path = str_replace( [ "\\", "/" ], DIRECTORY_SEPARATOR, $this->test_mode_expected_dir_path ); $this->test_mode_output_dir_path = str_replace( [ "\\", "/" ], DIRECTORY_SEPARATOR, $this->test_mode_output_dir_path ); $this->emptyDirectory( $this->test_mode_output_dir_path ); $this->processDirectory( $this->test_mode_input_dir_path, $this->test_mode_output_dir_path ); echo "Test results: " . count( $this->tests_successful ) . " successful, " . count( $this->tests_failed ) . " failed, " . count( $this->tests_unknown ) . " unknown." . PHP_EOL . PHP_EOL; if( isset( $this->test_mode_results_file_path ) && $this->test_mode_results_file_path && $this->test_mode_output_results_file ) { $successful_results = implode( "
" . PHP_EOL, $this->tests_successful ); $failed_results = implode( "
" . PHP_EOL, $this->tests_failed ); $unknown_results = implode( "
" . PHP_EOL, $this->tests_unknown ); $successful_results_count = count( $this->tests_successful ); $failed_results_count = count( $this->tests_failed ); $unknow_results_count = count( $this->tests_unknown ); $resultsHTML = << StaticPHP Test Results

StaticPHP Test Results


$successful_results_count Successful

$successful_results

$failed_results_count Failed

$failed_results

$unknow_results_count Unknown

$unknown_results
HTML; $this->outputFile( $this->test_mode_results_file_path, $resultsHTML ); } exit; } $this->emptyDirectory( $this->output_dir_path ); $this->processDirectory( $this->source_dir_path, $this->output_dir_path ); } private function emptyDirectory( String $path_to_directory ) { if( ! is_dir( $path_to_directory ) ) return; echo "Emptying Directory: " . $path_to_directory . PHP_EOL; $directory_items = scandir( $path_to_directory ); if( count( $directory_items ) == 2 ) echo "Directory Already Empty." . PHP_EOL; foreach( $directory_items as $directory_item ) { if( $directory_item == "." || $directory_item == ".." ) continue; $path_to_directory_item = $path_to_directory . DIRECTORY_SEPARATOR . $directory_item; if( is_dir( $path_to_directory_item ) ) { $this->emptyDirectory( $path_to_directory_item ); echo "Removing Directory: " . $path_to_directory_item . PHP_EOL; rmdir( $path_to_directory_item ); continue; } if( is_file( $path_to_directory_item ) ) { echo "Deleting File: " . $path_to_directory_item . PHP_EOL; unlink( $path_to_directory_item ); continue; } } if( count( $directory_items ) > 2 ) echo "Done." . PHP_EOL; } private function processDirectory( String $path_to_input_directory, String $path_to_output_directory ) { if( ! is_dir( $path_to_input_directory ) ) die( "Directory does not exist: " . $path_to_input_directory ); echo "Processing Directory: " . $path_to_input_directory . PHP_EOL; $directory_items = scandir( $path_to_input_directory ); if( ! is_dir( $path_to_output_directory ) && count( $directory_items ) > 2 ) { echo "Created New Directory: " . $path_to_output_directory . PHP_EOL; mkdir( $path_to_output_directory ); } if( count( $directory_items ) == 2 ) { echo "Input directory is empty. Nothing to process." . PHP_EOL; } foreach( $directory_items as $directory_item ) { if( $directory_item == "." || $directory_item == ".." ) continue; $path_to_input_directory_item = $path_to_input_directory . DIRECTORY_SEPARATOR . $directory_item; $path_to_output_directory_item = $path_to_output_directory . DIRECTORY_SEPARATOR . $directory_item; if( is_array( $this->items_to_ignore ) ) { foreach( $this->items_to_ignore as $item_to_ignore ) { if( $item_to_ignore != "" && strpos( $directory_item, $item_to_ignore ) !== false ) { echo "Ignoring Directory Item: " . $path_to_input_directory_item . PHP_EOL; continue( 2 ); } } } if( is_array( $this->items_to_passthrough ) ) { foreach( $this->items_to_passthrough as $item_to_passthrough ) { if( $item_to_passthrough != "" && strpos( $path_to_input_directory_item, $item_to_passthrough ) !== false && is_file( $path_to_input_directory_item ) ) { echo "Passing Through File: " . $path_to_input_directory_item . PHP_EOL; copy( $path_to_input_directory_item, $path_to_output_directory_item ); continue( 2 ); } } } if( is_dir( $path_to_input_directory_item ) ) { $this->processDirectory( $path_to_input_directory_item, $path_to_output_directory_item ); } if( is_file( $path_to_input_directory_item ) && substr( $directory_item, -4 ) == ".php" ) { $path_to_output_directory_item = substr( $path_to_output_directory_item, 0, -4 ) . ".html"; $this->processPHP( $path_to_input_directory_item, $path_to_output_directory_item ); continue; } if( is_file( $path_to_input_directory_item ) && substr( $directory_item, -5 ) == ".html" ) { $this->processHTML( $path_to_input_directory_item, $path_to_output_directory_item ); continue; } if( is_file( $path_to_input_directory_item ) && substr( $directory_item, -3 ) == ".md" ) { $path_to_output_directory_item = substr( $path_to_output_directory_item, 0, -3 ) . ".html"; $this->processMarkdown( $path_to_input_directory_item, $path_to_output_directory_item ); continue; } if( is_file( $path_to_input_directory_item ) && $directory_item == $this->bulk_redirects_filename ) { $redirect_list_file_contents = file_get_contents( $path_to_input_directory_item ); $redirect_list_file_contents = $this->convertEndOfLines( $redirect_list_file_contents ); $this->processBulkRedirects( $redirect_list_file_contents, $path_to_output_directory ); continue; } if( is_file( $path_to_input_directory_item ) ) { if( $this->minify_css === true && substr( $path_to_input_directory_item, -4 ) == ".css" ) { echo "Minifying CSS File: " . $path_to_input_directory_item . PHP_EOL; $css = file_get_contents( $path_to_input_directory_item ); $css_minified = $this->minifyCSS( $css ); if( $this->minify_css_inplace ) { $this->outputFile( $path_to_output_directory_item, $css_minified ); continue; } else { $this->outputFile( str_replace( ".css", ".min.css", $path_to_output_directory_item ), $css_minified ); $this->outputFile( $path_to_output_directory_item, $css ); continue; } } if( $this->minify_js === true && substr( $path_to_input_directory_item, -3 ) == ".js" ) { echo "Minifying JS File: " . $path_to_input_directory_item . PHP_EOL; $js = file_get_contents( $path_to_input_directory_item ); $js = $this->minifyJS( $js ); $this->outputFile( $path_to_output_directory_item, $js ); continue; } echo "Copying File: " . $path_to_input_directory_item . " to " . $path_to_output_directory_item . PHP_EOL; copy( $path_to_input_directory_item, $path_to_output_directory_item ); } } if( count( $directory_items ) > 2 ) { echo "Done.\n"; } } private function processMetaData( String $delimiter, String $input_contents, array &$metadata, String &$output_contents ) { if( ! isset( $metadata['staticphp_path'] ) ) $metadata['staticphp_path'] = __DIR__; $input_contents = $this->convertEndOfLines( $input_contents ); $input_lines = explode( PHP_EOL, $input_contents ); $input_line_count = count( $input_lines ); if( count( $input_lines ) > 1 && trim( $input_lines[ 0 ] ) == $delimiter ) { echo "Processing MetaData..." . PHP_EOL . PHP_EOL; unset( $input_lines[ 0 ] ); for( $line_number = 1; $line_number <= $input_line_count; $line_number++ ) { $input_line = trim( $input_lines[ $line_number ] ); unset( $input_lines[ $line_number ] ); if( $input_line == $delimiter ) break; if( ! strpos( $input_line, ":" ) ) continue; $data = explode( ":", $input_line, 2 ); $metadata_key = trim( $data[ 0 ] ); $metadata_value = trim( $data[ 1 ] ); echo "Setting MetaData Key: " . $metadata_key . PHP_EOL; echo "with matching value: " . $metadata_value . PHP_EOL . PHP_EOL; $metadata[ $metadata_key ] = $metadata_value; } $output_contents = join( PHP_EOL, $input_lines ); echo "End of MetaData." . PHP_EOL . PHP_EOL; } } private function processMetaDataPlaceHolders( String $delimiter, String $input_contents, array $metadata, String &$output_contents, String $prefix = 'metadata' ) { echo "Processing MetaData PlaceHolders..." . PHP_EOL; $pattern = '/' . $delimiter . '\s*' . $prefix . '\.(\S+)\s*' . $delimiter . '/'; $output_contents = preg_replace_callback ( $pattern, function( $matches ) use ( $metadata ) { $key = $matches[ 1 ]; if( array_key_exists( $key, $metadata ) ) { $value = $metadata[ $key ]; echo "Replacing " . $key . " with " . $value . PHP_EOL; return $value; } else { return $matches[ 0 ]; } }, $input_contents ); echo "Done.\n\n"; } private function processLayoutMetaData( array &$metadata, string $metaDataDelimiter, string &$layout_contents ) { if( ! isset( $metadata[ 'layout' ] ) ) return; if( ! is_string( $metadata[ 'layout' ] ) ) return; if( strlen( $metadata[ 'layout' ] ) <= 0 ) return; $base_layout_path = __DIR__ . DIRECTORY_SEPARATOR . $metadata[ 'layout' ]; if( ! is_file( $base_layout_path ) ) return; echo "Processing layout file: " . $base_layout_path . PHP_EOL; $layout_contents = file_get_contents( $base_layout_path ); $layout_metadata = array(); $this->processMetaData( $metaDataDelimiter, $layout_contents, $layout_metadata, $layout_contents ); $metadata = array_merge( $layout_metadata, $metadata ); } private function processContentPlaceHolder( array $metadata, string &$file_contents, string $layout_contents ) { // Check for no layout content and return early. if( ! $layout_contents ) return; // Set a safe default content placeholder if a custom one has not been set. if( ! isset( $metadata[ 'content_placeholder' ] ) || ! trim( $metadata[ 'content_placeholder' ] ) ) { $metadata[ 'content_placeholder' ] = "{{ content }}"; } // Update current page content with the layout content, replacing the placeholder with the content of current page, and log the action. echo "Replacing content placeholder with page content..." . PHP_EOL; $file_contents = str_replace( trim( $metadata['content_placeholder'] ), $file_contents, $layout_contents ); } private function processTemporaryFile( string $path_to_file, string &$file_contents, array $metadata ) { $temp_file_path = tempnam( dirname( $path_to_file ), "staticphp_" ); echo "Creating temporary file (" . $temp_file_path . ")..." . PHP_EOL; file_put_contents( $temp_file_path, $file_contents ); echo "Including temporary file..." . PHP_EOL; ob_start(); include $temp_file_path; $file_contents = ob_get_contents(); ob_end_clean(); echo "Removing temporary file..." . PHP_EOL; unlink( $temp_file_path ); } private function processOutputPath( string &$path_to_output_file, array $metadata, bool $friendly_urls, string $custom_output_path = null ) { // Check if output file is index.html and skip further processing. if( basename( $path_to_output_file ) == "index.html" ) return; // Is a custom output path defined? if( isset( $metadata['custom_output_path'] ) || $custom_output_path ) { if( isset( $metadata['custom_output_path'] ) ) { $path_to_output_file = $metadata['custom_output_path']; return; } $path_to_output_file = $custom_output_path; return; } // No custom output path defined, check for friendly URLs in metadata and give it priority. if( isset( $metadata['friendly_urls'] ) ) { if( $metadata['friendly_urls'] == "true" ) $friendly_urls = true; if( $metadata['friendly_urls'] == "false" ) $friendly_urls = false; } // Check if friendly URLs are enabled. if( $friendly_urls ) { // Check if a directory matching the output filename minus the extension exists. if( ! is_dir( substr( $path_to_output_file, 0, -5 ) ) ) { // Create a directory matching the output filename minus the extension. mkdir( substr( $path_to_output_file, 0, -5 ) ); } // Set path to output file to that of a directory with the same name minus extension with an index.html file inside it. $path_to_output_file = substr( $path_to_output_file, 0, -5 ) . DIRECTORY_SEPARATOR . "index.html"; } } private function outputFile( string $path_to_file, string $file_contents ) { echo "Outputting File: " . $path_to_file . PHP_EOL; $dir = dirname( $path_to_file ); // 1) Ensure the output directory exists if( $dir !== '' && ! is_dir( $dir ) ) { if( ! @mkdir( $dir, 0775, true ) ) { echo "✗ Could not create directory: '" . $dir . "'. Check permissions/ownership and available disk space." . PHP_EOL; return; } // Best effort permission set (ignore failure) @chmod( $dir, 0775 ); } // 2) Open the file for writing (binary mode avoids newline surprises on Windows) $fp = @fopen( $path_to_file, 'wb' ); if( $fp === false ) { echo "✗ Could not open file for writing: '" . $path_to_file . "'. Verify path, permissions, and that the parent directory is writable." . PHP_EOL; return; } // 3) Write the contents robustly $total = strlen( $file_contents ); $written = 0; // Handle large strings by writing in chunks while( $written < $total ) { $chunk = substr( $file_contents, $written, 1_048_576 ); // 1 MB chunks $n = @fwrite( $fp, $chunk ); if( $n === false ) { echo "✗ Write failed part-way through: '" . $path_to_file . "'. Output may be incomplete." . PHP_EOL; @fclose( $fp ); return; } $written += $n; } @fclose( $fp ); // 4) Set file permissions (0644 is typical for static files) @chmod( $path_to_file, 0644 ); echo "✓ Wrote " . $written . " bytes → " . $path_to_file . "." . PHP_EOL; } private function processPHP( $path_to_input_file, $path_to_output_file ) { if( ! isset( $staticphp_path ) ) $staticphp_path = __DIR__; if( ! is_file( $path_to_input_file ) ) return; echo "Processing PHP File: " . $path_to_input_file . PHP_EOL; ob_start(); include $path_to_input_file; $input_file_contents = ob_get_contents(); ob_end_clean(); // Convert end of lines $input_file_contents = $this->convertEndOfLines( $input_file_contents ); $metadata = array(); $this->processMetaData( $this->metaDataDelimiter, $input_file_contents, $metadata, $input_file_contents ); $layout_contents = ""; $this->processLayoutMetaData( $metadata, $this->metaDataDelimiter, $layout_contents ); if( isset( $metadata['layout'] ) && $metadata['layout'] && substr( $metadata['layout'], -4 ) == ".php" ) $this->processTemporaryFile( $metadata['layout'], $layout_contents, $metadata ); $this->processContentPlaceHolder( $metadata, $input_file_contents, $layout_contents ); $this->processMetaDataPlaceHolders( $this->metaDataDelimiter, $input_file_contents, $metadata, $input_file_contents ); $input_file_contents = $this->processFunctionalBlocks( $input_file_contents, $metadata ); if( ! isset( $friendly_urls ) ) $friendly_urls = $this->friendly_urls; if( isset( $custom_output_path ) ) $this->processOutputPath( $path_to_output_file, $metadata, $friendly_urls, $custom_output_path ); else $this->processOutputPath( $path_to_output_file, $metadata, $friendly_urls ); if( isset( $metadata[ 'remote_content_url' ] ) && isset( $metadata[ 'remote_content_placeholder' ] ) ) { $remote_content_url = $metadata[ 'remote_content_url' ]; $remote_content_placeholder = $metadata[ 'remote_content_placeholder' ]; $remote_content_from_url = $this->getRemoteContentFromURL( $remote_content_url ); $remote_content_from_url = $this->convertEndOfLines( $remote_content_from_url ); if( substr( $remote_content_url, -3 ) == ".md" ) { $remote_markdown_content = $this->convertMarkdownToHTML( $remote_content_from_url, $metadata, $friendly_urls, $path_to_output_file ); $input_file_contents = str_replace( $remote_content_placeholder, $remote_markdown_content, $input_file_contents ); } } if( isset( $metadata[ 'redirect' ] ) ) { // File Path, Old Path, New Destination $this->processRedirection( $path_to_output_file, str_replace( $this->output_dir_path, '', $path_to_output_file ), $metadata[ 'redirect' ] ); return; } if( $this->minify_html === true ) $input_file_contents = $this->minifyHTML( $input_file_contents ); if( $this->test_mode ) { $this->prepareForTest( $input_file_contents, $path_to_output_file ); return; } $this->outputFile( $path_to_output_file, $input_file_contents ); } private function processHTML( $path_to_input_file, $path_to_output_file ) { if( ! is_file( $path_to_input_file ) ) return; echo "Processing HTML File: " . $path_to_input_file . PHP_EOL; $input_file_contents = file_get_contents( $path_to_input_file ); // Convert end of lines $input_file_contents = $this->convertEndOfLines( $input_file_contents ); $metadata = array(); $this->processMetaData( $this->metaDataDelimiter, $input_file_contents, $metadata, $input_file_contents ); $layout_contents = ""; $this->processLayoutMetaData( $metadata, $this->metaDataDelimiter, $layout_contents ); if( isset( $metadata['layout'] ) && $metadata['layout'] && substr( $metadata['layout'], -4 ) == ".php" ) $this->processTemporaryFile( $metadata['layout'], $layout_contents, $metadata ); $this->processContentPlaceHolder( $metadata, $input_file_contents, $layout_contents ); $this->processMetaDataPlaceHolders( $this->metaDataDelimiter, $input_file_contents, $metadata, $input_file_contents ); $input_file_contents = $this->processFunctionalBlocks( $input_file_contents, $metadata ); if( ! isset( $friendly_urls ) ) $friendly_urls = $this->friendly_urls; if( isset( $custom_output_path ) ) $this->processOutputPath( $path_to_output_file, $metadata, $friendly_urls, $custom_output_path ); else $this->processOutputPath( $path_to_output_file, $metadata, $friendly_urls ); if( isset( $metadata[ 'remote_content_url' ] ) && isset( $metadata[ 'remote_content_placeholder' ] ) ) { $remote_content_url = $metadata[ 'remote_content_url' ]; $remote_content_placeholder = $metadata[ 'remote_content_placeholder' ]; $remote_content_from_url = $this->getRemoteContentFromURL( $remote_content_url ); $remote_content_from_url = $this->convertEndOfLines( $remote_content_from_url ); if( substr( $remote_content_url, -3 ) == ".md" ) { $remote_markdown_content = $this->convertMarkdownToHTML( $remote_content_from_url, $metadata, $friendly_urls, $path_to_output_file ); $input_file_contents = str_replace( $remote_content_placeholder, $remote_markdown_content, $input_file_contents ); } } if( isset( $metadata[ 'redirect' ] ) ) { // File Path, Old Path, New Destination $this->processRedirection( $path_to_output_file, str_replace( $this->output_dir_path, '', $path_to_output_file ), $metadata[ 'redirect' ] ); return; } if( $this->minify_html === true ) $input_file_contents = $this->minifyHTML( $input_file_contents ); if( $this->test_mode ) { $this->prepareForTest( $input_file_contents, $path_to_output_file ); return; } $this->outputFile( $path_to_output_file, $input_file_contents ); } private function processMarkdown( $path_to_input_file, $path_to_output_file ) { if( ! is_file( $path_to_input_file ) ) return; echo "Processing Markdown File: " . $path_to_input_file . PHP_EOL; $input_file_contents = file_get_contents( $path_to_input_file ); // Convert end of lines $input_file_contents = $this->convertEndOfLines( $input_file_contents ); $metadata = array(); $this->processMetaData( $this->metaDataDelimiter, $input_file_contents, $metadata, $input_file_contents ); if( ! isset( $friendly_urls ) ) $friendly_urls = $this->friendly_urls; $input_file_contents = $this->convertMarkdownToHTML( $input_file_contents, $metadata, $friendly_urls ); $layout_contents = ""; $this->processLayoutMetaData( $metadata, $this->metaDataDelimiter, $layout_contents ); if( isset( $metadata['layout'] ) && $metadata['layout'] && substr( $metadata['layout'], -4 ) == ".php" ) $this->processTemporaryFile( $metadata['layout'], $layout_contents, $metadata ); $this->processContentPlaceHolder( $metadata, $input_file_contents, $layout_contents ); $this->processMetaDataPlaceHolders( $this->metaDataDelimiter, $input_file_contents, $metadata, $input_file_contents ); $this->processOutputPath( $path_to_output_file, $metadata, $friendly_urls ); if( isset( $metadata[ 'remote_content_url' ] ) && isset( $metadata[ 'remote_content_placeholder' ] ) ) { $remote_content_url = $metadata[ 'remote_content_url' ]; $remote_content_placeholder = $metadata[ 'remote_content_placeholder' ]; $remote_content_from_url = $this->getRemoteContentFromURL( $remote_content_url ); $remote_content_from_url = $this->convertEndOfLines( $remote_content_from_url ); if( substr( $remote_content_url, -3 ) == ".md" ) { $remote_markdown_content = $this->convertMarkdownToHTML( $remote_content_from_url, $metadata, $friendly_urls ); $input_file_contents = str_replace( $remote_content_placeholder, $remote_markdown_content, $input_file_contents ); } } if( isset( $metadata[ 'redirect' ] ) ) { // File Path, Old Path, New Destination $this->processRedirection( $path_to_output_file, str_replace( $this->output_dir_path, '', $path_to_output_file ), $metadata[ 'redirect' ] ); return; } if( $this->minify_html === true ) $input_file_contents = $this->minifyHTML( $input_file_contents ); if( $this->test_mode ) { $this->prepareForTest( $input_file_contents, $path_to_output_file ); return; } $this->outputFile( $path_to_output_file, $input_file_contents ); } private function processBulkRedirects( String $redirect_list, String $path_to_output_directory, int $default_redirect_code = 307 ) { echo "Processing redirection list..." . PHP_EOL . PHP_EOL; $lines = explode( PHP_EOL, $redirect_list ); foreach( $lines as $line ) { $line = trim( $line ); if( $line === '' || substr( $line, 0, 1 ) === '#' ) continue; $line = preg_replace( '/\s+#.*$/', '', $line ); $line_parts = preg_split( '/\s+/', $line, 3, PREG_SPLIT_NO_EMPTY ); if( ! $line_parts || empty( $line_parts ) ) continue; if( count( $line_parts ) < 2 ) { echo "Skipping redirection due to missing destination: " . $line_parts[ 0 ] . PHP_EOL; continue; } [ $oldPath, $newDestination ] = $line_parts; $redirect_code = isset( $line_parts[ 2 ] ) && is_numeric( $line_parts[ 2 ] ) ? ( int ) $line_parts[ 2 ] : $default_redirect_code; if( ! in_array( $redirect_code, [ 301, 302, 307, 308 ], true ) ) { echo "Invalid redirect code (" . $redirect_code . "), using default code (" . $default_redirect_code . ")." . PHP_EOL; $redirect_code = $default_redirect_code; } if( substr( $oldPath, 0, 1 ) !== '/' && substr( $newDestination, 0, 1 ) !== '/' && substr( $newDestination, 0, 7 ) !== 'http://' && substr( $newDestination, 0, 8 ) !== 'https://' ) { echo "Skipping malformatted redirection: " . $oldPath . " -> " . $newDestination . PHP_EOL; continue; } $filePath = $path_to_output_directory . DIRECTORY_SEPARATOR . $oldPath; if( strpos( $filePath, '.' ) === false ) $filePath = $filePath . DIRECTORY_SEPARATOR . 'index.html'; $this->processRedirection( $filePath, $oldPath, $newDestination ); if( $this->generate_standard_redirects_file ) $this->generateStandardRedirectsFile( $oldPath, $newDestination, $redirect_code ); if( $this->generate_htaccess_redirections ) $this->generateHtaccessRedirections( $oldPath, $newDestination, $redirect_code ); } echo PHP_EOL . "Redirection list processed!" . PHP_EOL . PHP_EOL; } private function processRedirection( String $filePath, String $oldPath, String $newDestination ) { // Ensure the directory exists $fileDir = dirname( $filePath ); if ( ! is_dir( $fileDir ) ) { mkdir( $fileDir, 0755, true ); } // Create the redirection HTML file $htmlContent = << Redirecting...

If you are not redirected, click here.

HTML; $source_dir_path = $this->test_mode ? $this->test_mode_input_dir_path : $this->source_dir_path; if( is_file( $source_dir_path . DIRECTORY_SEPARATOR . $this->redirection_template_filename ) ) { $htmlContent = file_get_contents( $source_dir_path . DIRECTORY_SEPARATOR . $this->redirection_template_filename ); $htmlContent = str_replace( [ '$newDestination', '$oldPath' ], [ $newDestination, $oldPath ], $htmlContent ); } if( $this->minify_html === true ) $htmlContent = $this->minifyHTML( $htmlContent ); if( $this->test_mode ) { $this->prepareForTest( $htmlContent, $filePath ); echo "Redirect generated for $oldPath to $newDestination\n"; return; } $this->outputFile( $filePath, $htmlContent ); echo "Redirect generated for $oldPath to $newDestination\n"; } private function generateStandardRedirectsFile( String $oldPath, String $newDestination, int $redirect_code ) { $fileName = '_redirects'; $inputFilePath = $this->source_dir_path . DIRECTORY_SEPARATOR . $fileName; $outputFilePath = $this->output_dir_path . DIRECTORY_SEPARATOR . $fileName; if( $this->test_mode && $this->test_mode_input_dir_path ) $inputFilePath = $this->test_mode_input_dir_path . DIRECTORY_SEPARATOR . $fileName; if( $this->test_mode && $this->test_mode_output_dir_path ) $outputFilePath = $this->test_mode_output_dir_path . DIRECTORY_SEPARATOR . $fileName; echo "Generating redirect for " . $fileName . " file." . PHP_EOL; $file_contents = ''; if( is_file( $outputFilePath ) ) $file_contents = file_get_contents( $outputFilePath ) . PHP_EOL; else if( ! is_file( $outputFilePath ) && is_file( $inputFilePath ) ) $file_contents = file_get_contents( $inputFilePath ) . PHP_EOL; $file_contents .= $oldPath . ' ' . $newDestination . ' ' . $redirect_code; $this->outputFile( $outputFilePath, $file_contents ); } private function generateHtaccessRedirections( String $oldPath, String $newDestination, int $redirect_code ) { $fileName = '.htaccess'; $inputFilePath = $this->source_dir_path . DIRECTORY_SEPARATOR . $fileName; $outputFilePath = $this->output_dir_path . DIRECTORY_SEPARATOR . $fileName; if( $this->test_mode && $this->test_mode_input_dir_path ) $inputFilePath = $this->test_mode_input_dir_path . DIRECTORY_SEPARATOR . $fileName; if( $this->test_mode && $this->test_mode_output_dir_path ) $outputFilePath = $this->test_mode_output_dir_path . DIRECTORY_SEPARATOR . $fileName; $file_contents = ''; if( is_file( $outputFilePath ) ) $file_contents = file_get_contents( $outputFilePath ) . PHP_EOL; else if( ! is_file( $outputFilePath ) && is_file( $inputFilePath ) ) $file_contents = file_get_contents( $inputFilePath ) . PHP_EOL; $begin = '# BEGIN StaticPHP Bulk Redirects'; $end = '# END StaticPHP Bulk Redirects'; $blockPattern = '/' . preg_quote( $begin, '/' ) . '.*?' . preg_quote( $end, '/' ) . '/s'; $existingRules = []; $pre = $file_contents; $hasBlock = preg_match( $blockPattern, $file_contents, $m ) === 1; if( $hasBlock ) { $block = $m[ 0 ]; if( preg_match_all( '/^\s*RewriteRule\s+(\S+)\s+(\S+)\s+\[R=(\d{3}),END\]\s*$/m', $block, $mm, PREG_SET_ORDER ) ) { foreach( $mm as $match ) { $pattern = $match[1]; $pattern = preg_replace( '/^\^|\$$/', '', $pattern ); $pattern = preg_replace( '/\\\\(.)/', '$1', $pattern ); $dest = $match[ 2 ]; $rcode = ( int ) $match[ 3 ]; $p = $pattern; if( strlen( $p ) >= 2 && $p[ 0 ] === '^' ) { $p = substr( $p, 1 ); } if( substr( $p, -1 ) === '$' ) { $p = substr( $p, 0, -1 ); } $p = str_replace( [ '\/' ], [ '/' ], $p ); $fromKey = '/' . ltrim( $p, '/' ); $existingRules[ $fromKey ] = [ 'to' => $dest, 'code' => $rcode ]; } } $pre = preg_replace( $blockPattern, '', $file_contents, 1 ); } $existingRules[ $oldPath ] = [ 'to' => $newDestination, 'code' => $redirect_code ]; ksort( $existingRules, SORT_STRING ); $lines = []; $lines[] = $begin; $lines[] = '# Managed by StaticPHP. There is no need to manually edit between these markers.'; $lines[] = ''; $lines[] = ''; $lines[] = "\tRewriteEngine On"; foreach( $existingRules as $f => $info ) { $dest = $info[ 'to' ]; $rc = $info[ 'code' ]; $pattern = '^' . preg_quote( ltrim( $f, '/' ), '#' ) . '$'; $lines[] = "\t" . sprintf( 'RewriteRule %s %s [R=%d,END]', $pattern, $dest, $rc ); } $lines[] = ''; $lines[] = ''; $lines[] = $end; $newBlock = implode( PHP_EOL, $lines ) . PHP_EOL; $pre = rtrim( $pre ); if( $pre ) $pre .= PHP_EOL . PHP_EOL; $newContents = $pre . $newBlock; $this->outputFile( $outputFilePath, $newContents ); } private function processFunctionalBlocks( String $content, array $metadata ) { echo "Processing Functional Blocks...\n"; $delimiter = $this->metaDataDelimiter; $pattern = '/' . preg_quote($delimiter) . ' (\w+)\(([^)]*)\) ' . preg_quote($delimiter) . '(.*?)' . preg_quote($delimiter) . ' end\1 ' . preg_quote($delimiter) . '/s'; $output = preg_replace_callback( $pattern, function( $matches ) use ( $delimiter, $metadata ) { $funcName = $matches[ 1 ]; $paramStr = $matches[ 2 ]; $blockContent = $matches[ 3 ]; switch( $funcName ) { case 'loop': $blockOutput = $this->processLoopFunctionalBlock( $this->parseFunctionalBlockParameters( $paramStr ), $blockContent ); if( $blockOutput !== null && $blockOutput !== "" ) { return $blockOutput; // Replaced Content } break; case 'if': $blockOutput = $this->processIfFunctionalBlock( $paramStr, $blockContent, $metadata ); if( $blockOutput !== null ) { return $blockOutput; // Replaced Content } break; } return $matches[ 0 ]; // Original Content }, $content ); echo "...Functional Blocks Processed." . PHP_EOL; return $output; } private function processLoopFunctionalBlock( array $params, String $loopContent ) { if( ! isset( $params[ 'dir' ] ) || ! is_dir( $params[ 'dir' ] ) ) { return null; } echo "Processing Loop Functional Block..." . PHP_EOL; $dir = __DIR__ . DIRECTORY_SEPARATOR . $params[ 'dir' ]; $dir = str_replace( [ "\\", "/" ], DIRECTORY_SEPARATOR, $dir ); $output = array(); $output = $this->processLoopDir( $dir, $params, $loopContent, $output ); if( isset( $params[ 'json' ] ) ) { $jsonFilePath = str_replace( [ "\\", "/" ], DIRECTORY_SEPARATOR, $params[ 'json' ] ); $jsonFilePathParts = explode( DIRECTORY_SEPARATOR, $jsonFilePath ); $currentJsonFilePath = ""; for( $cjfp = 0; $cjfp < count( $jsonFilePathParts ) -1; $cjfp++ ) { $currentJsonFilePath .= $jsonFilePathParts[ $cjfp ] . DIRECTORY_SEPARATOR; if( ! is_dir( $currentJsonFilePath ) && $cjfp != count( $jsonFilePathParts ) -1 ) { mkdir( $currentJsonFilePath ); } } if( $this->test_mode ) { $this->prepareForTest( json_encode( $output ), $jsonFilePath ); } else { echo "Outputting JSON File: " . $jsonFilePath . PHP_EOL; file_put_contents( $jsonFilePath, json_encode( $output ) ); echo "JSON File Complete.\n"; } } $output_str = ""; foreach( $output as $output_item ) { $output_str .= $output_item['outputContent']; } echo "...Loop Functional Block Processed." . PHP_EOL; return $output_str; } private function processIfFunctionalBlock( String $paramStr, String $content, array $metadata ) { echo "Processing If Functional Block..." . PHP_EOL; $condition_state = true; $params = preg_split( "/(\s*)(&&)(\s*)/", $paramStr ); foreach( $params as $param ) { $param = trim( $param ); if( strpos( $param, '==' ) ) { $param = preg_split( "/(\s*)(==)(\s*)/", $param ); $param_key = $param[ 0 ]; $param_value = $param[ 1 ]; if( ! array_key_exists( $param_key, $metadata ) ) { $condition_state = false; } if( substr( trim( $param_value ), 0, 1 ) != "\"" && substr( trim( $param_value ), -1, 1 ) != "\"" ) { $condition_state = false; } if( array_key_exists( $param_key, $metadata ) && $metadata[ $param_key ] != substr( $param_value, 1, -1 ) ) { $condition_state = false; } } else if( strpos( $param, '!=' ) ) { $param = preg_split( "/(\s*)(!=)(\s*)/", $param ); $param_key = $param[ 0 ]; $param_value = $param[ 1 ]; if( ! array_key_exists( $param_key, $metadata ) ) { $condition_state = false; } if( substr( trim( $param_value ), 0, 1 ) != "\"" && substr( trim( $param_value ), -1, 1 ) != "\"" ) { $condition_state = false; } if( array_key_exists( $param_key, $metadata ) && $metadata[ $param_key ] == substr( $param_value, 1, -1 ) ) { $condition_state = false; } } else { if( substr( trim( $param ), 0, 1 ) == "!" && array_key_exists( trim( $param ), $metadata ) ) { $condition_state = false; } if( substr( trim( $param ), 0, 1 ) != "!" && ! array_key_exists( trim( $param ), $metadata ) ) { $condition_state = false; } } } echo "...If Functional Block Processed." . PHP_EOL; if( $condition_state ) return $content; return ""; } private function processLoopDir( String $dirPath, array $params, String $loopContent, array $output = array() ) { if( ! is_dir( $dirPath ) ) return; echo "Processing Loop Directory: " . $dirPath . PHP_EOL; $dirContents = scandir( $dirPath ); if( isset( $params['sort'] ) && $params['sort'] == "ascending" ) { sort( $dirContents ); } elseif( isset( $params['sort'] ) && $params['sort'] == "descending" ) { rsort( $dirContents ); } foreach( $dirContents as $dirItem ) { if( $dirItem === '.' || $dirItem === '..' ) { continue; } $dirItemPath = $dirPath . DIRECTORY_SEPARATOR . $dirItem; if( is_dir( $dirItemPath ) ) { $output = $this->processLoopDir( $dirItemPath, $params, $loopContent, $output ); continue; } if( ! is_file( $dirItemPath ) ) { continue; } if( substr( $dirItem, -4 ) !== ".php" && substr( $dirItem, -5 ) !== ".html" ) { continue; } if( is_array( $this->items_to_ignore ) ) { foreach( $this->items_to_ignore as $item_to_ignore ) { if( $item_to_ignore != "" && strpos( $dirItemPath, $item_to_ignore ) !== false ) { echo "Loop Ignoring Item: " . $dirItemPath . PHP_EOL; continue( 2 ); } } } if( isset( $params[ 'ignores' ] ) ) { $ignore_items = explode( ";", $params[ 'ignores' ] ); foreach( $ignore_items as $ignore_item ) { $ignore_item = trim( $ignore_item ); if( strpos( $dirItemPath, $ignore_item ) !== false ) { echo "Loop Ignoring Item: " . $dirItemPath . PHP_EOL; continue( 2 ); } } } $fileContents = file_get_contents( $dirItemPath ); $thisLoopContent = $loopContent; $metadata = array(); $this->processMetaData( $this->metaDataDelimiter, $fileContents, $metadata, $fileContents ); unset( $metadata['staticphp_path'] ); if( isset( $params[ 'filter-key' ] ) && $params[ 'filter-key' ] && isset( $metadata[ $params[ 'filter-key' ] ] ) && $metadata[ $params[ 'filter-key' ] ] && isset( $params[ 'filter-value' ] ) && $metadata[ $params[ 'filter-key' ] ] != $params[ 'filter-value' ] ) { continue; } if( isset( $params[ 'filter-key' ] ) && $params[ 'filter-key' ] && ( ! isset( $metadata[ $params[ 'filter-key' ] ] ) || ! $metadata[ $params[ 'filter-key' ] ] ) ) { continue; } $source_dir_path = $this->test_mode ? $this->test_mode_input_dir_path : $this->source_dir_path; $output_dir_path = $this->test_mode ? $this->test_mode_output_dir_path : $this->output_dir_path; $source_dir_path = str_replace( [ "\\", "/" ], DIRECTORY_SEPARATOR, $source_dir_path ); $output_dir_path = str_replace( [ "\\", "/" ], DIRECTORY_SEPARATOR, $output_dir_path ); $fileOutputPath = str_replace( [ $source_dir_path, ".php" ], [ $output_dir_path, ".html" ], $dirItemPath ); $fileURI = $fileOutputPath; if( ! isset( $friendly_urls ) ) $friendly_urls = $this->friendly_urls; $this->processOutputPath( $fileURI, $metadata, $friendly_urls ); $fileURI = str_replace( [ $output_dir_path, "index.html", "\\" ], [ "", "", "/" ], $fileURI ); $metadata[ 'uri' ] = $fileURI; $this->processMetaDataPlaceHolders( $this->metaDataDelimiter, $loopContent, $metadata, $thisLoopContent, 'loop' ); if( isset( $params[ 'content_placeholder' ] ) && $params[ 'content_placeholder' ] ) { $thisLoopContent = str_replace( $params[ 'content_placeholder' ], $fileContents, $thisLoopContent ); } $toOutput['metadata'] = $metadata; $toOutput['fileContents'] = $fileContents; $toOutput['outputContent'] = $thisLoopContent; $output[] = $toOutput; echo "...Loop Directory Processed." . PHP_EOL; } return $output; } private function parseFunctionalBlockParameters( String $paramStr ) { $params = []; $paramPairs = explode( ',', $paramStr ); foreach( $paramPairs as $pair ) { list( $key, $value ) = explode( '=', $pair, 2 ); $params[ trim( $key ) ] = trim( trim( $value ), "'\"" ); } return $params; } private function minifyHTML( String $html ) { echo "Minifying HTML..." . PHP_EOL; // Convert all HTML end of lines to use system specific end of lines using PHP_EOL $html = $this->convertEndOfLines( $html ); $preserved_html_tags = array(); // Extract HTML tags to be preserved if( count( $this->minify_html_tags_to_preserve ) >= 1 ) { foreach( $this->minify_html_tags_to_preserve as $tag_to_preserve ) { preg_match_all( "#<{$tag_to_preserve}[^>]*>.*?#is", $html, $preserve_tag_matches ); $preserve_tag_matches = isset( $preserve_tag_matches[ 0 ] ) ? $preserve_tag_matches[ 0 ] : array(); foreach( $preserve_tag_matches as $preserve_tag_num => $preserve_tag_match ) { $preserved_tag_key = "__STATICPHP_PRESERVED_{$tag_to_preserve}_CODE_BLOCK_{$preserve_tag_num}__"; $preserved_html_tags[ $preserved_tag_key ] = $preserve_tag_match; $html = str_replace( $preserve_tag_match, $preserved_tag_key, $html ); } } } // Remove comments $html = preg_replace( '//s', '', $html ); // Remove whitespace between tags $html = preg_replace( '/>\s+<', $html ); // Remove unnecessary spaces $html = preg_replace( '/\s+/', ' ', $html ); // Restore preserved HTML tags foreach( $preserved_html_tags as $preserved_tag_key => $preserved_tag ) { $html = str_replace( $preserved_tag_key, $preserved_tag, $html ); } return $html; } private function minifyCSS( String $css ) { echo "Minifying CSS..." . PHP_EOL; // Remove comments $css = preg_replace( '!/\*.*?\*/!s', '', $css ); // Remove spaces, newlines, and tabs $css = preg_replace( '/\s*([{}|:;,])\s*/', '$1', $css ); // Remove trailing semicolons in CSS blocks $css = preg_replace( '/;}/', '}', $css ); // Remove extra whitespace $css = preg_replace( '/\s+/', ' ', $css ); return $css; } private function minifyJS( String $js ) { echo "JavaScript Minification is disabled until a bug in the minification process can be fixed." . PHP_EOL; return $js; } private function convertEndOfLines( $text ) { return preg_replace( "/\r\n|\r|\n/", PHP_EOL, $text ); } private function getRemoteContentFromURL( String $URL ) { echo "Attempting to fetch from remote URL: " . $URL . PHP_EOL; $remote_file_headers = @get_headers( $URL ); if( ! $remote_file_headers && strpos( $remote_file_headers[ 0 ], '200' ) === false ) { echo "Unable to fetch from remote URL. Remote content is either unavailable or returned non-200 HTTP code." . PHP_EOL; return ""; } $remote_content = @file_get_contents( $URL ); if( $remote_content === false ) { echo "Unable to fetch from remote URL. Error: " . error_get_last() . PHP_EOL; return ""; } return $remote_content; } private function convertMarkdownToHTML( String $markdown, array $metadata, bool $friendly_urls ) { $lines = explode( PHP_EOL, $markdown ); $codeblockName = ""; $isCodeblock = false; $listStarts = array(); $listEnds = array(); $inList = false; $endOfList = false; // Start of List Detection Code for( $l = 0; $l < count( $lines ); $l++ ) { if( preg_match( "/^\s*[-*+]\s+/", $lines[ $l ] ) ) { if( ! $inList ) { $listStarts[] = $l; $inList = true; } } else { if( $inList ) { $listEnds[] = $l - 1; $inList = false; } } } if( $inList ) { $listEnds[] = $l - 1; } // End of List Detection Code for( $l = 0; $l < count( $lines ); $l++ ) { if( preg_match( "/\`\`\`(.*)/", $lines[ $l ], $matches ) ) { if( ! $isCodeblock ) { $codeblockName = $matches[ 1 ]; $isCodeblock = true; if( $codeblockName ) $lines[ $l ] = preg_replace( "/\`\`\`(.*)/", "
", $lines[ $l ] );
					else
						$lines[ $l ] = preg_replace( "/\`\`\`(.*)/", "
", $lines[ $l ] );

					continue;
				}
			}

			if( preg_match( "/\`\`\`/", $lines[ $l ], $matches ) )
			{
				if( $isCodeblock )
				{
					$lines[ $l ] = preg_replace( "/\`\`\`/", "
", $lines[ $l ] ); $isCodeblock = false; continue; } } if( $isCodeblock ) { $lines[ $l ] = htmlentities( $lines[ $l ] ); continue; } $inlineCodeTokens = array(); $inlineCodeIndex = 0; // First: match and replace double backtick inline code blocks (e.g. `` ` ``) if( preg_match_all( '/``(.*?)``/', $lines[$l], $matches, PREG_SET_ORDER ) ) { foreach( $matches as $match ) { $token = '{{INLINECODE' . $inlineCodeIndex++ . '}}'; $inlineCodeTokens[$token] = '' . htmlentities( $match[ 1 ] ) . ''; $lines[ $l ] = str_replace( $match[ 0 ], $token, $lines[ $l ] ); } } // Second: match and replace single backtick inline code blocks (e.g. `code`) if( preg_match_all( '/`(.*?)`/', $lines[$l], $matches, PREG_SET_ORDER ) ) { foreach( $matches as $match ) { $token = '{{INLINECODE' . $inlineCodeIndex++ . '}}'; $inlineCodeTokens[ $token ] = '' . htmlentities( $match[ 1 ] ) . ''; $lines[$l] = str_replace( $match[ 0 ], $token, $lines[ $l ] ); } } if( preg_match( "/^\s*-\s+(.*)/", $lines[ $l ], $matches ) ) { $lines[ $l ] = "
  • " . htmlspecialchars( $matches[ 1 ] ) . "
  • "; } if( in_array( $l, $listStarts ) ) { $lines[ $l ] = ''; $endOfList = true; } if( preg_match( "/(#{6}\s)(.*)/", $lines[ $l ] ) ) { $lines[ $l ] = preg_replace( "/(#{6}\s)(.*)/", "
    $2
    ", $lines[ $l ] ); continue; } if( preg_match( "/(#{5}\s)(.*)/", $lines[ $l ] ) ) { $lines[ $l ] = preg_replace( "/(#{5}\s)(.*)/", "
    $2
    ", $lines[ $l ] ); continue; } if( preg_match( "/(#{4}\s)(.*)/", $lines[ $l ] ) ) { $lines[ $l ] = preg_replace( "/(#{4}\s)(.*)/", "

    $2

    ", $lines[ $l ] ); continue; } if( preg_match( "/(#{3}\s)(.*)/", $lines[ $l ] ) ) { $lines[ $l ] = preg_replace( "/(#{3}\s)(.*)/", "

    $2

    ", $lines[ $l ] ); continue; } if( preg_match( "/(#{2}\s)(.*)/", $lines[ $l ] ) ) { $lines[ $l ] = preg_replace( "/(#{2}\s)(.*)/", "

    $2

    ", $lines[ $l ] ); continue; } if( preg_match( "/(#{1}\s)(.*)/", $lines[ $l ] ) ) { $lines[ $l ] = preg_replace( "/(#{1}\s)(.*)/", "

    $2

    ", $lines[ $l ] ); continue; } if( preg_match( "/^\s*(-{3,}|\*{3,}|_{3,})\s*$/", $lines[ $l ] ) ) { $lines[ $l ] = preg_replace( "/^\s*(-{3,}|\*{3,}|_{3,})\s*$/", "
    ", $lines[ $l ] ); continue; } $lines[ $l ] = preg_replace( "/\*\*([^\*]+)\*\*/", "$1", $lines[ $l ] ); $lines[ $l ] = preg_replace( "/\_\_([^\_]+)\_\_/", "$1", $lines[ $l ] ); $lines[ $l ] = preg_replace( "/\*([^\*]+)\*/", "$1", $lines[ $l ] ); $lines[ $l ] = preg_replace( "/\_([^\_]+)\_/", "$1", $lines[ $l ] ); $lines[ $l ] = preg_replace( "/\~\~([^\~]+)\~\~/", "$1", $lines[ $l ] ); $lines[ $l ] = preg_replace( "/\!\[([^\]]+)\]\(([^\"\)]+) \"([^\"]+)\"\)/", "\"$1\"", $lines[ $l ] ); $lines[ $l ] = preg_replace( "/\!\[([^\]]+)\]\(([^\"\)]+)\)/", "\"$1\"", $lines[ $l ] ); $lines[ $l ] = preg_replace_callback ( '/\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)/', function( $matches ) { $text = $matches[ 1 ]; $href = $matches[ 2 ]; $title = isset( $matches[ 3 ] ) ? ' title="' . htmlspecialchars( $matches[ 3 ] ) . '"' : ''; $external = false; if( strpos( $href, '://' ) ) $external = true; return '' . htmlspecialchars( $text ) . ''; }, $lines[ $l ] ); if( $lines[ $l ] == "" ) continue; if( ! $inList ) { if( $l != 0 && $lines[ $l -1 ] != "" && substr( $lines[ $l -1 ], -4 ) == "

    " ) $lines[ $l -1 ] = substr( $lines[ $l -1 ], 0, -4 ) . "
    "; elseif( $l == 0 || ( $l != 0 && substr( $lines[ $l -1 ], -4 ) != "
    " ) ) $lines[ $l ] = "

    " . $lines[ $l ]; $lines[ $l ] .= "

    "; } if( ! empty( $inlineCodeTokens ) ) { $lines[ $l ] = str_replace( array_keys( $inlineCodeTokens ), array_values( $inlineCodeTokens ), $lines[ $l ] ); } if( $endOfList ) { $inList = false; $endOfList = false; } } $html = implode( PHP_EOL, $lines ); return $html; } private function prepareForTest( $input_contents, $path_to_output_file ) { echo "Preparing for test..." . PHP_EOL; $path_to_output_file = str_replace( [ "\\", "/" ], DIRECTORY_SEPARATOR, $path_to_output_file ); if( substr( $path_to_output_file, 0, strlen( __DIR__ . DIRECTORY_SEPARATOR ) ) != __DIR__ . DIRECTORY_SEPARATOR ) $path_to_output_file = __DIR__ . DIRECTORY_SEPARATOR . $path_to_output_file; echo "Path to Output file: " . $path_to_output_file . PHP_EOL; $expected_file_path = str_replace( $this->test_mode_output_dir_path, $this->test_mode_expected_dir_path, $path_to_output_file ); if( ! is_file( $expected_file_path ) ) { echo "ERROR: Expected file missing: " . $expected_file_path . PHP_EOL; $this->runTest( $input_contents, "", $path_to_output_file ); return; } $expected_file_contents = file_get_contents( $expected_file_path ); $this->runTest( $input_contents, $expected_file_contents, $path_to_output_file ); } private function runTest( $input_contents, $expected_contents, $path_to_output_file ) { echo "Running test..." . PHP_EOL; $path_to_output_dir = substr( $path_to_output_file, 0, strrpos( $path_to_output_file, DIRECTORY_SEPARATOR ) + 1 ); $output_file_name = substr( $path_to_output_file, strrpos( $path_to_output_file, DIRECTORY_SEPARATOR ) + 1 ); $input_contents = $this->convertEndOfLines( $input_contents ); $expected_contents = $this->convertEndOfLines( $expected_contents ); if( $input_contents != $expected_contents ) { if( $expected_contents == "" ) { $path_to_output_file = $path_to_output_dir . "UNKNOWN." . $output_file_name; $this->tests_unknown[] = $path_to_output_file; } else { $path_to_output_file = $path_to_output_dir . "FAILED." . $output_file_name; $this->tests_failed[] = $path_to_output_file; } $this->outputFile( $path_to_output_file, $input_contents ); echo "TEST FAILED: Check output file!" . PHP_EOL; return; } $path_to_output_file = $path_to_output_dir . "PASSED." . $output_file_name; $this->tests_successful[] = $path_to_output_file; $this->outputFile( $path_to_output_file, $input_contents ); echo "TEST PASSED!" . PHP_EOL; return; } } function detect_and_parse_cli_args_into_configurable_options( array $args ) { if( count( $args ) < 1 ) return; // Detect Named Flags if( substr( $args[ 0 ], 0, 2 ) == "--" ) { return parse_cli_named_flags_into_configurable_options( $args ); } // Otherwise assume normal cli args return parse_cli_args_into_configurable_options( $args ); } function parse_cli_named_flags_into_configurable_options( array $args ) { $configurable_options = array(); foreach( $args as $arg ) { if( substr( $arg, 0, 2 ) == "--" ) $arg = substr( $arg, 2 ); if( ! strpos( $arg, "=" ) ) continue; $arg = explode( "=", $arg, 2 ); $configurable_key = $arg[ 0 ]; $configurable_value = $arg[ 1 ]; $configurable_options[ $configurable_key ] = $configurable_value; } return $configurable_options; } function parse_cli_args_into_configurable_options( array $args ) { $configurable_options = array(); if( isset( $args[ 0 ] ) ) $configurable_options[ 'source_dir_path' ] = $args[ 0 ]; if( isset( $args[ 1 ] ) ) $configurable_options[ 'output_dir_path' ] = $args[ 1 ]; if( isset( $args[ 2 ] ) ) $configurable_options[ 'items_to_ignore' ] = $args[ 2 ]; if( isset( $args[ 3 ] ) ) $configurable_options[ 'friendly_urls '] = $args[ 3 ]; if( isset( $args[ 4 ] ) ) $configurable_options[ 'metadata_delimiter' ] = $args[ 4 ]; if( isset( $args[ 5 ] ) ) $configurable_options[ 'minify_html' ] = $args[ 5 ]; if( isset( $args[ 6 ] ) ) $configurable_options[ 'minify_css' ] = $args[ 6 ]; if( isset( $args[ 7 ] ) ) $configurable_options[ 'minify_js' ] = $args[ 7 ]; if( isset( $args[ 8 ] ) ) $configurable_options[ 'minify_html_tags_to_preserve' ] = $args[ 8 ]; if( isset( $args[ 9 ] ) ) $configurable_options[ 'bulk_redirects_filename' ] = $args[ 9 ]; if( isset( $args[ 10 ] ) ) $configurable_options[ 'redirection_template_filename' ] = $args[ 10 ]; if( isset( $args[ 11 ] ) ) $configurable_options[ 'minify_css_inplace' ] = $args[ 11 ]; if( isset( $args[ 12 ] ) ) $configurable_options[ 'items_to_passthrough' ] = $args[ 12 ]; return $configurable_options; } if( isset( $argv[ 0 ] ) && basename( $argv[ 0 ] ) == basename( __FILE__ ) ) { $configurable_options = array(); unset( $argv[ 0 ] ); $argv = array_values( $argv ); if( count( $argv ) >= 1 ) { $configurable_options = detect_and_parse_cli_args_into_configurable_options( $argv ); } new StaticPHP( $configurable_options ); }