Make WordPress Core

Ticket #63793: 63793.patch

File 63793.patch, 6.7 KB (added by mokhaled, 5 months ago)
  • wp-includes/class-wp-dependencies.php

     
    245245        public function do_items( $handles = false, $group = false ) {
    246246                $handles = false === $handles ? $this->queue : (array) $handles;
    247247                $this->all_deps( $handles );
     248
     249                // Optimize script loading order for performance while maintaining dependencies
     250                if ( $this instanceof WP_Scripts ) {
     251                        $this->optimize_loading_order();
     252                }
     253
    248254                foreach ( $this->to_do as $key => $handle ) {
    249255                        if ( ! in_array( $handle, $this->done, true ) && isset( $this->registered[ $handle ] ) ) {
    250256                                if ( $this->do_item( $handle, $group ) ) {
  • wp-includes/class-wp-scripts.php

     
    1515        public $base_url;
    1616
    1717        /**
     18         * Whether script loading order optimization is enabled.
     19         *
     20         * @since 6.8.0
     21         */
     22        private $optimize_loading_order_enabled = true;
     23
     24        /**
    1825         * @var string
    1926         */
    2027        public $content_url;
     
    548555
    549556                return $eligible_strategy;
    550557        }
     558
     559        /**
     560         * Optimizes script loading order to reduce parser blocking time.
     561         *
     562         * Reorders scripts so that async and defer scripts are processed first,
     563         * allowing them to download in parallel while blocking scripts execute,
     564         * thereby reducing DOMContentLoaded timing.
     565         *
     566         * @since 6.8.0
     567         */
     568        private function optimize_loading_order() {
     569                if ( ! $this->optimize_loading_order_enabled || empty( $this->to_do ) ) {
     570                        return;
     571                }
     572
     573                $start_time = microtime( true );
     574                $original_order = $this->to_do;
     575
     576                // Group scripts by loading priority
     577                $script_priorities = array();
     578                $dependency_map = array();
     579
     580                // Build dependency map and calculate priorities
     581                foreach ( $this->to_do as $handle ) {
     582                        if ( isset( $this->registered[ $handle ] ) ) {
     583                                $script = $this->registered[ $handle ];
     584                                $strategy = $this->get_eligible_loading_strategy( $handle, 'blocking' );
     585                                $priority = $this->calculate_loading_priority( $handle, $strategy );
     586
     587                                $script_priorities[ $handle ] = $priority;
     588                                $dependency_map[ $handle ] = $script->deps ?? array();
     589                        }
     590                }
     591
     592                // Sort scripts by priority while maintaining dependency order
     593                $optimized_order = $this->sort_with_dependencies( $script_priorities, $dependency_map );
     594
     595                // Update the to_do array with optimized order
     596                $this->to_do = array_values( $optimized_order );
     597
     598                // Performance monitoring hook
     599                if ( function_exists( 'do_action' ) ) {
     600                        $end_time = microtime( true );
     601                        do_action( 'wp_script_optimization_complete', array(
     602                                'execution_time' => $end_time - $start_time,
     603                                'scripts_processed' => count( $original_order ),
     604                                'original_order' => $original_order,
     605                                'optimized_order' => $this->to_do,
     606                        ) );
     607                }
     608        }
     609
     610        /**
     611         * Calculates loading priority for a script based on its strategy and characteristics.
     612         *
     613         * Lower numbers = higher priority (loaded first)
     614         * Priority order: async (1) -> defer (2) -> no-deps blocking (3) -> deps blocking (4)
     615         *
     616         * @since 6.8.0
     617         *
     618         * @param string $handle   Script handle.
     619         * @param string $strategy Loading strategy ('async', 'defer', or 'blocking').
     620         * @return int Loading priority.
     621         */
     622        private function calculate_loading_priority( $handle, $strategy ) {
     623                switch ( $strategy ) {
     624                        case 'async':
     625                                return 1; // Highest priority - non-blocking, can load immediately
     626
     627                        case 'defer':
     628                                return 2; // Second priority - non-blocking but ordered
     629
     630                        case 'blocking':
     631                        default:
     632                                // Blocking scripts get lower priority, but consider dependencies
     633                                $script = $this->registered[ $handle ];
     634                                $has_deps = ! empty( $script->deps );
     635                                $has_inline = ! empty( $script->extra['after'] ) || ! empty( $script->extra['before'] );
     636
     637                                if ( $has_deps || $has_inline ) {
     638                                        return 4; // Lowest priority - blocking with dependencies/inline scripts
     639                                }
     640
     641                                return 3; // Low priority - simple blocking scripts
     642                }
     643        }
     644
     645        /**
     646         * Sorts scripts by priority while maintaining dependency order.
     647         *
     648         * Uses topological sorting to ensure dependencies are processed before
     649         * their dependents, while optimizing for loading performance.
     650         *
     651         * @since 6.8.0
     652         *
     653         * @param array $priorities    Script priorities keyed by handle.
     654         * @param array $dependencies  Dependency map keyed by handle.
     655         * @return array Optimized script order.
     656         */
     657        private function sort_with_dependencies( $priorities, $dependencies ) {
     658                $sorted = array();
     659                $visited = array();
     660                $visiting = array();
     661
     662                // Group scripts by priority
     663                $priority_groups = array();
     664                foreach ( $priorities as $handle => $priority ) {
     665                        $priority_groups[ $priority ][] = $handle;
     666                }
     667
     668                // Sort each priority group while respecting dependencies
     669                ksort( $priority_groups );
     670                foreach ( $priority_groups as $priority => $handles ) {
     671                        foreach ( $handles as $handle ) {
     672                                $this->topological_sort_visit( $handle, $dependencies, $visited, $visiting, $sorted );
     673                        }
     674                }
     675
     676                return $sorted;
     677        }
     678
     679        /**
     680         * Performs topological sort visit for dependency resolution.
     681         *
     682         * @since 6.8.0
     683         *
     684         * @param string $handle       Current script handle.
     685         * @param array  $dependencies Dependency map.
     686         * @param array  &$visited     Visited handles.
     687         * @param array  &$visiting    Currently visiting handles (cycle detection).
     688         * @param array  &$sorted      Sorted result array.
     689         */
     690        private function topological_sort_visit( $handle, $dependencies, &$visited, &$visiting, &$sorted ) {
     691                if ( isset( $visiting[ $handle ] ) ) {
     692                        // Circular dependency detected - maintain original order
     693                        return;
     694                }
     695
     696                if ( isset( $visited[ $handle ] ) ) {
     697                        return;
     698                }
     699
     700                $visiting[ $handle ] = true;
     701
     702                // Visit dependencies first
     703                if ( isset( $dependencies[ $handle ] ) ) {
     704                        foreach ( $dependencies[ $handle ] as $dep ) {
     705                                // Visit dependency if it exists in our registered scripts or dependencies
     706                                if ( isset( $dependencies[ $dep ] ) || isset( $this->registered[ $dep ] ) ) {
     707                                        $this->topological_sort_visit( $dep, $dependencies, $visited, $visiting, $sorted );
     708                                }
     709                        }
     710                }
     711
     712                unset( $visiting[ $handle ] );
     713                $visited[ $handle ] = true;
     714                $sorted[] = $handle;
     715        }
     716
     717        /**
     718         * Disables script loading order optimization.
     719         *
     720         * @since 6.8.0
     721         */
     722        public function disable_loading_order_optimization() {
     723                $this->optimize_loading_order_enabled = false;
     724        }
     725
     726        /**
     727         * Enables script loading order optimization.
     728         *
     729         * @since 6.8.0
     730         */
     731        public function enable_loading_order_optimization() {
     732                $this->optimize_loading_order_enabled = true;
     733        }
    551734}