Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CLEANUP] Refactor parseCssRules: parse all CSS #528

Merged
merged 5 commits into from
Mar 31, 2018

Conversation

JakeQZ
Copy link
Contributor

@JakeQZ JakeQZ commented Feb 22, 2018

Refactored parseCssRules so that it can now parse an entire CSS string,
including @media rules. It now returns two arrays, in an array with the
following keys:

  • "inlineable" => The inlineable rules that would have been previously returned;
  • "uninlineable" => The uninlineable rules, currently those from within @media
    rules.

The original line number information from the CSS is available in both arrays,
so the original ordering of all the rules (that will be required for #280) can
be determined.

A temporary method combineCssMediaRules has been added to recombine the @media
rules back into a string of CSS as expected by copyCssWithMediaToStyleNode,
pending refactoring of that method to instead accept a parsed rule array as its
parameter. This does mean that, for now, the rules within each @media rule are
parsed again.

Refactored `parseCssRules` so that it can now parse an entire CSS string,
including @media rules.  It now returns two arrays, in an array with the
following keys:
- "inlineable" => The inlineable rules that would have been previously returned;
- "uninlineable" => The uninlineable rules, currently those from within @media
  rules.

The original line number information from the CSS is available in both arrays,
so the original ordering of all the rules (that will be required for MyIntervals#280) can
be determined.

A temporary method `combineCssMediaRules` has been added to recombine the @media
rules back into a string of CSS as expected by `copyCssWithMediaToStyleNode`,
pending refactoring of that method to instead accept a parsed rule array as its
parameter.  This does mean that, for now, the rules within each @media rule are
parsed again.
@JakeQZ
Copy link
Contributor Author

JakeQZ commented Feb 22, 2018

As you'll see, the code in combineCssMediaRules is somewhat messy and I think would benefit from a CssConcatenator class to abstract the functionality for combining rules with the same media query, selectors or declarations block. (This functionality will still be required after refactoring copyCssWithMediaToStyleNode, then by the refactored method itself.) This I propose to add in the next incremental PR, though it could be included in this PR if desired...


$cssRules = [
'inlineable' => [],
'uninlineable' => []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a trailing comma also after the last element of a multiline array. This makes appending elements possible without having to modify the existing line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

];
$selectors = explode(',', $cssRule[1]);
foreach ($selectors as $selector) {
// don't process pseudo-elements and behavioral (dynamic) pseudo-classes;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have this part (within the foreach loop of $selector, or most of it) in a separate private method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved the outer loop and regex matching into a separate method, so the loops are now very similar to how they were prior to this PR.

'uninlineable' => []
];
// keep track of where each rule appears in the file, since order is important
$key = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we have this as the key in the corresponding loop?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently it needs to increment with the inner loop and carry on incrementing with each iteration of the outer loop. However, it should be possible to have one double-loop doing the regex matching and array_merge-ing the results, then a second single loop processing the matches...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed as per comment above.

*
* @return string
*/
private function combineCssMediaRules(array $cssRules)
Copy link
Contributor

@oliverklee oliverklee Feb 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also please split up this method? It's far too long. This would also allow to get rid of the comments (if the methods are well-named).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why I'd like to add a CssConcatenator class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now using CssConcatenator.

Copy link
Contributor

@oliverklee oliverklee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, I propose you do this kind of changes to one file first, and then do it to the other file once the changes to one file are final.

@oliverklee
Copy link
Contributor

As for the CssConcatenator class, we will only be able to have this for the CssInliner class, not for Emogrifier anymore (as the Emogrifier class is Composer-optional, and the class will go away for version 4.0.0).

@oliverklee oliverklee closed this Feb 22, 2018
@oliverklee
Copy link
Contributor

Oops, wrong button. Sorry, Reopening.

Copy link
Contributor

@oliverklee oliverklee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also please rebase to resolve the merge conflicts?

@JakeQZ
Copy link
Contributor Author

JakeQZ commented Feb 22, 2018

As for the CssConcatenator class, we will only be able to have this for the CssInliner class, not for Emogrifier anymore (as the Emogrifier class is Composer-optional, and the class will go away for version 4.0.0).

Would something like

        // support use without autoload
        if (!class_exists('Pelago\\Emogrifier\\CssConcatenator')) {
            require_once __DIR__ . '/Emogrifier/CssConcatenator.php';
        }

not be possible within the Emogrifier class? I understand the need to support those not using autoload, but is it also a requirement to support grabbing just the .php file rather than the src directory (tree)?

@oliverklee
Copy link
Contributor

No, that would be okay for me.

@JakeQZ
Copy link
Contributor Author

JakeQZ commented Feb 23, 2018

I've added a CssConcatenator class as a separate PR (#530). I'll come back to this one when that is agreed.

These will be merged as a separate PR once the equivalent changes to
Emogrifier.php have been agreed.
- Moved the regex matching part of `parseCssRules` (along with outer loop) into
  a separate private method;
- Added trailing comma in multiline array definition;
- Use `CssConcatenator` in `combineCssMediaRules`.
@JakeQZ
Copy link
Contributor Author

JakeQZ commented Mar 29, 2018

Now made the changes suggested in review. Also rebased and reverted changes to CssInliner.php which can be merged as a separate PR once agreed.

Copy link
Contributor

@oliverklee oliverklee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very good. Just needs the finishing polish!


$this->caches[static::CACHE_KEY_CSS][$cssKey] = $cssRules;
}

return $this->caches[static::CACHE_KEY_CSS][$cssKey];
}

/**
* Parse a string of CSS into the media query, selectors and declarations for each ruleset in order
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Third person, please ("Parses") and a period at the end of sentence.

if ($cssRule['media'] === '') {
$cssRules['inlineable'][] = $parsedCssRule;
} else {
$cssRules['uninlineable'][] = $parsedCssRule;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can shorten this to something like this:

$ruleType = ($cssRule['media'] === '') ? 'inlineable' : 'uninlineable';
$cssRules[$ruleType] = $parsedCssRule;

Copy link
Contributor

@oliverklee oliverklee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just done the last two polishing tasks myself. Thanks for this refactoring, @JakeQZ! ❤️

@oliverklee oliverklee merged commit e043b5f into MyIntervals:master Mar 31, 2018
JakeQZ added a commit to JakeQZ/emogrifier that referenced this pull request Apr 1, 2018
oliverklee pushed a commit that referenced this pull request Apr 2, 2018
@JakeQZ JakeQZ deleted the refactor/parseCssRules branch April 2, 2018 21:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants