Skip to Content

render_scss()

Render Sass

PHP March 16, 2017

Usage

PHP
nebula()->render_scss($child)

Parameters

$child
(Optional) (Boolean) If checking child theme directory
Default: false

Request or provide clarification »

Additional Notes

This function is typically called automatically as needed and generally does not need to be called manually.

Source File

Located in /libs/Utilities/Sass.php on line 125.

3 Hooks

Find these filters and actions in the source code below to hook into them. Use do_action() and add_filter() in your functions file or plugin.

Filters
"pre_nebula_render_scss"
"nebula_scss_variables"
Need a new filter hook? Request one here.

Actions
"nebula_before_sass_compile"
Need a new action hook? Request one here.

Note: This function contains 4 to-do comments.

PHP
        public function render_scss($location_name=false, $location_paths=false, $force_all=false){
            $override = apply_filters('pre_nebula_render_scss', null, $location_name, $location_paths, $force_all);
            if ( isset($override) ){return;}

            global $sass_errors;

            if ( $this->get_option('scss') && !empty($location_name) && !empty($location_paths) ){
                $this->timer('Sass (' . $location_name . ')', 'start', 'Sass');

                //Force reprocessing all files if "all" string is the only parameter
                if ( strtolower($location_name) === 'all' ){
                    $force_all = true;
                }

                //Require SCSSPHP
                require_once get_template_directory() . '/inc/vendor/scssphp/scss.inc.php'; //SCSSPHP is a compiler for SCSS 3.x
                $this->scss = new \ScssPhp\ScssPhp\Compiler();

                //Register import directories
                if ( !is_array($location_paths['imports']) ){
                    $location_paths['imports'] = array($location_paths['imports']); //Convert to an array if passes as a string
                }
                foreach ( $location_paths['imports'] as $imports_directory ){
                    $this->scss->addImportPath($imports_directory);
                }

                //Set compiling options
                $this->scss->setFormatter('ScssPhp\ScssPhp\Formatter\Compressed'); //Minify CSS (while leaving "/*!" comments for WordPress).

                //Source Maps
                $this->scss->setSourceMap(1); //0 = No .map, 1 = Inline .map, 2 = Output .map file
                $this->scss->setSourceMapOptions(array(
                    'sourceMapBasepath' => ABSPATH, //Difference between file & URL locations, removed from all source paths in .map
                    'sourceRoot' => '/', //Added to source path locations if needed
                ));

                //Variables
                $nebula_scss_variables = array(
                    'parent_partials_directory' => get_template_directory() . '/assets/scss/partials/',
                    'child_partials_directory' => get_stylesheet_directory() . '/assets/scss/partials/',
                    'template_directory' => '"' . get_template_directory_uri() . '"',
                    'stylesheet_directory' => '"' . get_stylesheet_directory_uri() . '"',
                    'this_directory' => '"' . $location_paths['uri'] . '"',
                    'primary_color' => $this->get_color('primary_color', false, '#0098d7'),
                    'secondary_color' => $this->get_color('secondary_color', false, '#95d600'),
                    'background_color' => $this->get_color('background_color', false, '#f6f6f6'),
                );

                $all_scss_variables = apply_filters('nebula_scss_variables', $nebula_scss_variables);
                $this->scss->setVariables($nebula_scss_variables);

                //Imports/Partials (find the last modified time)
                $latest_import = 0;
                foreach ( glob($imports_directory . '*') as $import_file ){
                    if ( filemtime($import_file) > $latest_import ){
                        $latest_import = filemtime($import_file);

                        if ( $latest_import > $this->latest_scss_mtime ){
                            $this->latest_scss_mtime = $latest_import;
                        }
                    }
                }

                $this->add_custom_scssphp_functions($this->scss); //Add custom PHP functions that can be used in Sass

                do_action('nebula_before_sass_compile', $location_paths); //Allow modification of files before looping through to compile Sass

                //Compile each SCSS file
                foreach ( glob($location_paths['directory'] . '/assets/scss/*.scss') as $scss_file ){ //@TODO "Nebula" 0: Change to glob_r() but will need to create subdirectories if they don't exist.
                    $scss_file_path_info = pathinfo($scss_file);
                    $debug_name = str_replace(WP_CONTENT_DIR, '', $scss_file_path_info['dirname']) . '/' . $scss_file_path_info['basename'];
                    $this->timer('Sass File ' . $debug_name);

                    //Skip file conditions (only if not forcing all)
                    if ( empty($force_all) ){
                        //@todo "Nebula" 0: Add hook here so other functions/plugins can add stipulations of when to skip files. Maybe an array instead?
                        $is_admin_file = (!$this->is_admin_page() && !$this->is_login_page()) && in_array($scss_file_path_info['filename'], array('login', 'admin', 'tinymce')); //If viewing front-end, skip WP admin files.
                        if ( $is_admin_file ){
                            continue;
                        }
                    }

                    //If file exists, and has .scss extension, and doesn't begin with "_".
                    if ( is_file($scss_file) && $scss_file_path_info['extension'] === 'scss' && $scss_file_path_info['filename'][0] !== '_' ){
                        $css_filepath = ( $scss_file_path_info['filename'] === 'style' )? $location_paths['directory'] . '/style.css': $location_paths['directory'] . '/assets/css/' . $scss_file_path_info['filename'] . '.css'; //style.css to the root directory. All others to the /css directory in the /assets/scss directory.
                        wp_mkdir_p($location_paths['directory'] . '/assets/css'); //Create the /css directory (in case it doesn't exist already).

                        //Update the last SCSS file modification time (if later than the latest yet)
                        if ( filemtime($scss_file) > $this->latest_scss_mtime ){
                            $this->latest_scss_mtime = filemtime($scss_file);
                        }

                        //If style.css has been edited after style.scss, save backup but continue compiling SCSS
                        if ( (is_child_theme() && $location_name !== 'parent' ) && ($scss_file_path_info['filename'] === 'style' && file_exists($css_filepath) && $this->get_data('scss_last_processed') != '0' && $this->get_data('scss_last_processed')-filemtime($css_filepath) < -30) ){
                            copy($css_filepath, $css_filepath . '.bak'); //Backup the style.css file to style.css.bak
                            if ( $this->is_dev() || current_user_can('manage_options') ){
                                global $scss_debug_ref;
                                $scss_debug_ref = $location_name . ':';
                                $scss_debug_ref .= ($this->get_data('scss_last_processed')-filemtime($css_filepath));
                                add_action('wp_head', array($this, 'scss_console_warning')); //Call the console error note
                            }
                        }

                        //If .css file doesn't exist, or is older than .scss file (or any partial), or is debug mode, or forced
                        if ( !file_exists($css_filepath) || filemtime($scss_file) > filemtime($css_filepath) || $latest_import > filemtime($css_filepath) || $this->is_debug() || $force_all ){
                            ini_set('memory_limit', '512M'); //Increase memory limit for this script. //@todo Nebula 0: Remove this when possible...
                            WP_Filesystem();
                            global $wp_filesystem;
                            $existing_css_contents = ( file_exists($css_filepath) )? $wp_filesystem->get_contents($css_filepath) : '';

                            //If the correlating .css file doesn't contain a comment to prevent overwriting
                            if ( !strpos(strtolower($existing_css_contents), 'scss disabled') ){
                                $this_scss_contents = $wp_filesystem->get_contents($scss_file); //Copy SCSS file contents

                                //Catch fatal compilation errors when PHP v7.0+ to provide additional information without crashing
                                if ( version_compare(phpversion(), '7.0.0', '>=') ){ //@todo: remove this conditional once PHP7 is widely enough used.
                                    try {
                                        $compiled_css = $this->scss->compile($this_scss_contents, $scss_file); //Compile the SCSS
                                    } catch (\Throwable $error){
                                        $unprotected_array = (array) $error;
                                        $prefix = chr(0) . '*' . chr(0);

                                        $sass_errors[] = array(
                                            'file' => $scss_file,
                                            'message' => $unprotected_array[$prefix . 'message']
                                        );

                                        continue; //Skip the file that contains errors
                                    }
                                } else {
                                    $compiled_css = $this->scss->compile($this_scss_contents, $scss_file); //Compile the SCSS
                                }

                                $enhanced_css = $this->scss_post_compile($compiled_css); //Compile server-side variables into SCSS
                                $wp_filesystem->put_contents($css_filepath, $enhanced_css); //Save the rendered CSS.
                                $this->update_data('scss_last_processed', time());
                                $this->timer('Sass File ' . $debug_name, 'end');
                            }
                        }
                    }
                }

                $this->timer('Sass (' . $location_name . ')', 'end');
            }
        }

Override

To override this PHP function, use this hook in your child theme or plugin ("my_custom" can be changed):

PHP
add_filter('pre_nebula_render_scss', 'my_custom_render_scss', 10, 2); //The last integer must be 1 more than the actual parameters
function my_custom_render_scss($null, $child){ //$null is required, but can be ignored
    //Write your own code here

    return true; //Return true to prevent the original function from running afterwords
}

You can completely disable this PHP function with a single line actions:

PHP
 add_filter('pre_nebula_render_scss', '__return_false');