Last active
October 17, 2019 04:19
-
-
Save Robbertdk/dbab8f2a4e38bdac067b2f2105d6977e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Kirki Sass Compiler | |
* | |
* Create a CSS file based on a SCSS-file and Kirki variables | |
* File gets saved in the public folder with a cache buster. | |
*/ | |
namespace App\Kirki; | |
use ScssPhp\ScssPhp\Compiler; | |
class DynamicCSS { | |
private $css_dir; | |
private $css_file_name; | |
private $css_file_url; | |
public function __construct($settings) { | |
if (!class_exists( 'Kirki' )) return; | |
$this->css_dir = $this->get_file_dir($settings['foldername']); | |
$this->css_file_name = $this->get_file_name($settings['filename']); | |
$this->css_file_url = $this->get_cached_file_url($settings['foldername']); | |
// Create an CSS file with the initial values on theme change | |
add_action('switch_theme', array( $this, 'remove_css_directory' ), 999); | |
add_action('after_switch_theme', array( $this, 'refresh_css')); | |
add_action( 'init', [$this, 'init'], 999 ); | |
} | |
/** | |
* Hooks up the compiler | |
* | |
* @return void | |
*/ | |
public function init() { | |
global $wp_customize; | |
// If whe're in the customizer, add the styles directly into the head | |
// Thereby we see the changes on partial refresh, before the customize_save_after hook is triggered | |
if ( $wp_customize ) { | |
add_action( 'wp_enqueue_scripts', array( $this, 'get_inline_styles' ), 999 ); | |
// Create a new CSS file on customizer settings save | |
add_action( 'customize_save_after', array( $this, 'refresh_css' ) ); | |
return; | |
} | |
// Enqueue the script | |
add_action( 'wp_enqueue_scripts', array($this, 'enqueue_dynamic_css'), 999); | |
} | |
/** | |
* Compiles CSS and adds it as inline style. | |
* | |
* Used during style changes in the Customizer | |
* | |
* @return void | |
*/ | |
public function get_inline_styles() { | |
$styles = $this->compile_scss(); | |
if ( !empty($styles) ) { | |
wp_enqueue_style( 'dynamic-css', $this->css_file_url ); | |
wp_add_inline_style( 'dynamic-css', $styles ); | |
} | |
} | |
/** | |
* Returns the path to the css file directory. | |
* | |
* @return string | |
*/ | |
private function get_file_dir($folder = 'dynamic-css') { | |
$upload_dir = wp_upload_dir(); | |
return $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $folder; | |
} | |
/** | |
* Removes the css folder | |
* | |
* @return void | |
*/ | |
public function remove_css_directory() { | |
global $wp_filesystem; | |
if ( empty( $wp_filesystem ) ) { | |
require_once( ABSPATH . '/wp-admin/includes/file.php' ); | |
WP_Filesystem(); | |
} | |
return $wp_filesystem->rmdir( $this->css_dir, true); | |
} | |
/** | |
* Returns the file name adjusted for multisite. | |
* | |
* @return string | |
*/ | |
private function get_file_name($basename = 'styles') { | |
// Add blog ID if on multisite | |
global $blog_id; | |
$blog_id = ( is_multisite() && $blog_id > 1 ) ? '_blog-' . $blog_id : null; | |
return $basename . $blog_id . '.css'; | |
} | |
/** | |
* Returns the path to the css file with the cache buster in the filename. | |
* | |
* @param string cachebuster | |
* @return string | |
*/ | |
private function get_cached_file_path($buster = false) { | |
// Get cache buster | |
if (!$buster) { | |
$buster = $this->get_cache_buster(); | |
} | |
$file_name = str_replace('.css', '-' .$buster . '.css', $this->css_file_name); | |
return $this->css_dir . DIRECTORY_SEPARATOR . $file_name; | |
} | |
/** | |
* Returns the url to the css file name, adjusted for multisite. | |
* @param string foldername | |
* @return string | |
*/ | |
private function get_cached_file_url($foldername = 'dynamic-styles'){ | |
$upload_dir = wp_upload_dir(); | |
$cache_buster = $this->get_cache_buster(); | |
$file_name = str_replace('.css', '-' . $cache_buster . '.css', $this->css_file_name); | |
$css_url = trailingslashit( $upload_dir['baseurl'] ) . $foldername . '/' . $file_name; | |
// Take care of domain mapping | |
if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ) { | |
if ( function_exists( 'domain_mapping_siteurl' ) && function_exists( 'get_original_url' ) ) { | |
$mapped_domain = domain_mapping_siteurl( false ); | |
$original_domain = get_original_url( 'siteurl' ); | |
$css_url = str_replace( $original_domain, $mapped_domain, $css_url ); | |
} | |
} | |
// Strip protocols | |
$css_url = str_replace( 'https://', '//', $css_url ); | |
$css_url = str_replace( 'http://', '//', $css_url ); | |
return $css_url; | |
} | |
/** | |
* Returns the cache buster id | |
* @return String | |
*/ | |
private function get_cache_buster() { | |
$filename = $this->css_dir . DIRECTORY_SEPARATOR . 'assets.json'; | |
$buster = file_exists($filename) ? json_decode(file_get_contents($filename), true) : false; | |
return $buster ? $buster['assets'] : Null; | |
} | |
/** | |
* Creates a json file with the cache buster | |
* | |
* @param string Cache buster id | |
* @return String | |
*/ | |
private function set_cache_buster($id) { | |
global $wp_filesystem; | |
// Initialize the Wordpress filesystem. | |
if ( empty( $wp_filesystem ) ) { | |
require_once( ABSPATH . '/wp-admin/includes/file.php' ); | |
WP_Filesystem(); | |
} | |
$buster = ['assets' => $id]; | |
$buster = json_encode($buster); | |
if ( ! $wp_filesystem->put_contents( $this->css_dir . DIRECTORY_SEPARATOR . 'assets.json', $buster, FS_CHMOD_FILE ) ) { | |
error_log('Can\'t create Cache buster. Something went wrong'); | |
return false; | |
} | |
} | |
/** | |
* Delete all css-files, except the one with the new cache buster | |
* @return String | |
*/ | |
private function delete_old_styles($new_file_buster) { | |
$files = glob( $this->css_dir . DIRECTORY_SEPARATOR . '*.css' ); | |
foreach ( $files as $file ) { | |
// If the file has the new buster, don't delete it (caus its new)! | |
if (is_file( $file ) && strpos(basename($file), $new_file_buster) === false) { | |
unlink( $file ); | |
} | |
} | |
} | |
/** | |
* Enqueue the dynamic CSS. | |
*/ | |
public function enqueue_dynamic_css() { | |
wp_enqueue_style( 'dynamic-css', $this->css_file_url ); | |
} | |
public function refresh_css() { | |
global $wp_filesystem; | |
// Initialize the Wordpress filesystem. | |
if ( empty( $wp_filesystem ) ) { | |
require_once( ABSPATH . '/wp-admin/includes/file.php' ); | |
WP_Filesystem(); | |
} | |
$content = "/********* Compiled - Do not edit *********/\n" . $this->compile_scss(); | |
// Take care of domain mapping | |
if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ) { | |
if ( function_exists( 'domain_mapping_siteurl' ) && function_exists( 'get_original_url' ) ) { | |
$mapped_domain = domain_mapping_siteurl( false ); | |
$mapped_domain = str_replace( 'https://', '//', $domain_mapping ); | |
$mapped_domain = str_replace( 'http://', '//', $mapped_domain ); | |
$original_domain = get_original_url( 'siteurl' ); | |
$original_domain = str_replace( 'https://', '//', $original_domain ); | |
$original_domain = str_replace( 'http://', '//', $original_domain ); | |
$content = str_replace( $original_domain, $mapped_domain, $content ); | |
} | |
} | |
// Strip protocols | |
$content = str_replace( 'https://', '//', $content ); | |
$content = str_replace( 'http://', '//', $content ); | |
// Create a unique buster | |
$buster = uniqid(); | |
// Create file | |
if ($this->can_write()) { | |
if ( ! $wp_filesystem->put_contents( $this->get_cached_file_path($buster), $content, FS_CHMOD_FILE ) ) { | |
// Fail! | |
error_log('Can\'t create CSS. Something went wrong'); | |
return false; | |
} | |
// Update the cache buster file | |
$this->set_cache_buster($buster); | |
// Delete old files | |
$this->delete_old_styles($buster); | |
} else { | |
error_log('Can\'t create CSS. File does not exist or is not writable'); | |
} | |
} | |
/* | |
* Determines if the CSS file is writable. | |
*/ | |
public function can_write() { | |
// Does the folder exist? | |
if ( file_exists( $this->css_dir ) ) { | |
// Folder exists, but is the folder writable? | |
if ( ! is_writable( $this->css_dir ) ) { | |
// Folder is not writable. | |
error_log('CSS file could not be written.'); | |
return false; | |
} | |
} else { | |
// Can we create the folder? | |
// returns true if yes and false if not. | |
return wp_mkdir_p( $this->css_dir ); | |
} | |
// all is well! | |
return true; | |
} | |
/** | |
* Compiles the Kirki Variables and SCSS-file to a CSS-string | |
* | |
* @return string | |
*/ | |
public function compile_scss() | |
{ | |
$scss = new Compiler(); | |
$scss->setImportPaths( get_stylesheet_directory() . '/assets/styles' ); | |
$scss->setFormatter( 'ScssPhp\ScssPhp\Formatter\Compressed' ); | |
$variables = \Kirki_Util::get_variables(); | |
$vars = ''; | |
foreach ( $variables as $variable => $value ) { | |
if (is_string($value)) { | |
$vars .= '$' . $variable . ':' . $value . ';'; | |
} | |
} | |
// Create SCSS string from Kirki variables and scss functions we've written | |
$scss_string = $vars . ' | |
@import "theme"'; | |
// Compile SCSS string to CSS string | |
$css = $scss->compile( $scss_string ); | |
return $css; | |
} | |
} | |
$compiler = new DynamicCSS([ | |
'filename' => 'styles', | |
'foldername' => 'dynamic-css', | |
]); |
I don't get notifications for these comments, so it suprises me when I see new comments over here. So, sorry for the delay in my answers.
@ecksite good point, thanks! I've added it to the gist.
@jessekafor I don't see the problem. I use the latest version of Kirki and generated css file of this gist is added as an external script and not inlined. Can you eleborate on the problem you encounter?
Because I can't add commit messages to changes on this script, I just leave them here:
Today I've commited the following changes:
- The Leafo\ScssPhp composer package is archived, development of that package will be continued under the package ScssPhp\ScssPhp. So I swapped that.
- Remove unneeded error log message
- Add check for the Kirki class so it won't fail when the Kirki plugin is not activated.
- Add string check for variables added via the customizer as mentioned by @ecksite
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Robbertdk How could we now use the script when Kirki’s output is basic inline css in the header?