I thought this was going to be pretty simple, but I've been struggling with it now for a while. I know there are CSS parser classes out there that can acheive what I want to do... but I don't need 95% of the functionality they have, so they're not really feasible and would just be too heavy.
All I need to be able to do is pull out any class and/or ID names used in a CSS file via regex. Here's the regex I thought would work, but hasn't.
[^a-z0-9][\w]*(?=\s)
When run against my sample:
.stuffclass {
color:#fff;
background:url('blah.jpg');
}
.newclass{
color:#fff;
background:url('blah.jpg');
}
.oldclass {
color:#fff;
background:url('blah.jpg');
}
#blah.newclass {
color:#fff;
background:url('blah.jpg');
}
.oldclass#blah{
color:#fff;
background:url('blah.jpg');
}
.oldclass #blah {
color:#fff;
background:url('blah.jpg');
}
.oldclass .newclass {
text-shadow:1px 1px 0 #fff;
color:#fff;
background:url('blah.jpg');
}
.oldclass:hover{
color:#fff;
background:url('blah.jpg');
}
.newclass:active {
text-shadow:1px 1px 0 #000;
}
It does match most of what I want, but it's also including the curly brackets and doesn't match the ID's. I need to match the ID's and Classes separately when conjoined. So basically #blah.newclass would be 2 separate matches: #blah AND .newclass.
Any ideas?
===================
FINAL SOLUTION
I wound up using 2 regex to first strip out everything between { and }, then simply matched the selectors based on the remaining input.
Here's a full working example:
//Grab contents of css file
$file = file_get_contents('css/style.css');
//Strip out everything between { and }
$pattern_one = '/(?<=\{)(.*?)(?=\})/s';
//Match any and all selectors (and pseudos)
$pattern_two = '/[\.|#][\w]([:\w]+?)+/';
//Run the first regex pattern on the input
$stripped = preg_replace($pattern_one, '', $file);
//Variable to hold results
$selectors = array();
//Run the second regex pattern on $stripped input
$matches = preg_match_all($pattern_two, $stripped, $selectors);
//Show the results
print_r(array_unique($selectors[0]));
[^a-z0-9][\w]+(?=\s)
I changed your * to a + match
It works fine in RegEXR - an awesome regex development tool: http://gskinner.com/RegExr/ (See bottom right of window to download the desktop version)
This version is based on nealio82's, but adding pseudo-selectors:
[^a-z0-9][\w:-]+(?=\s)
/(?<!:\s)[#.][\w]*/
some thing like this? excludes the #FFFFFF color stuff...
The solution posted by OP works, though it didn't work for me with CSS classes that had hyphens. As such, I've amended the second pattern to work more effectively:
$pattern_two = '/[\.|#]([A-Za-z0-9_\-])*(\s?)+/';
Related
Need some suggestions for the methodology to achieve the following:
My current script gets text and if it has URL(s) then it replaces them. The issue is I want to truncate the URLS(s) so they do not break the width of a table or unsightly line break to fit them.
$text = file_get_contents("temp.txt");
$link = preg_replace('#(https?://([-\w\.]+)+(:\d+)?(/([-\w/_\.]*(\?\S+)?)?)?)#', '$1', $text);
echo $link;
I am concerned that if I substr() the $link then it won't work if multiple URLs are found. Can you PHP the $1 in the replacement? Any alternatives?
Use preg_replace_callback to modify the match and replacement. This returns the first 10 characters as an example:
$link = preg_replace_callback('#(https?://([-\w\.]+)+(:\d+)?(/([-\w/_\.]*(\?\S+)?)?)?)#',
function($m) {
return ''.substr($m[1], 0, 10).'';
},
$text);
This kind of problem can also be solved on client side using css (I assume you are speaking about the html element table in your question).
To do that, you have to give your cell a fixed size and to set the display property to inline-block. Then you can define the behaviour of the cell when a word is too long using the white-space, overflow and text-overflow properties.
Example:
<html>
<head>
<style>
.mytable td:nth-child(2) {
display: inline-block;
width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
</head>
<body>
<table class="mytable">
<tr><td>abcd</td><td>www.smallurl.jp</td><td>efgh</td></tr>
<tr><td>ijkl</td><td>www.a-giant.url/larger/than/the/cell/width</td><td>mnop</td></tr>
</table>
</body>
</html>
I'm building the backend to a web application, where it is possible to change the design of a page. Once the user has setup the design the way they like it, they should be able to save it and the new design would be written to the specific file.
For this reason, I will need to replace all the characters between { and } after a certain string, which would be the name of the class.
So a very simple concept, say the following class was in a seperate file which I load as a view, style.php. I would need to go from:
from
<style>
.bonus {
bottom: 6px;
left: 247px;
}
</style>
to
<style>
.bonus {
bottom: 20px;
left: 450px;
}
</style>
Could someone recommend me on the best way to
a) find a string in a file,
b) when that is found, replace everything between two strings right after the first string.
Thank you.
I don't like the concept of the user making changes to the actual file very much. There are a lot of safer methods by which a user could create and maintain a custom template without them actually making changes to a PHP file.
What about storing the user's CSS in a field in a database? Then you'd simply need to do something like:
<?php
$css = getCSSByUserId($userId); //function runs query on DB to get user-specific CSS
/* $css = ".bonus {
bottom: 20px;
left: 450px;
}" */
?>
<style>
<?php echo $css; ?>
</style>
If you really want to edit the actual file, you'd do it something like this:
<?
$file = "/path/to/file.php";
//The user's replacement CSS
$replace = '.bonus {
bottom: 20px;
left: 450px;
}';
$str = file_get_contents($file);
$str = preg_replace('/\.bonus \{.*\}/U', $replace, $str);
$res = fopen($file, 'w');
fwrite($res, $str);
fclose($res);
?>
I checked the regex here http://www.quanetic.com/Regex and it works.
i'm working on a script that generates multiple CSS into one. And Here is the script.
$full_string = "";
foreach($allfiles as $curfile => $file) {
$file = $PATH[$curfile] . $file;
$file_open = fopen($file , 'r');
$concat = "\n" . fread($file_open , filesize($file)) . "\n";
$full_string .= $concat;
fclose($file_open);
}
return $full_string;
Here i'm combining all the CSS files into one. But the problem now is i have to compare the current CSS($file) with another css(let's consider it as overrider.css). And if $file is having a style like,
h1 {
color: white;
background: teal;
FONT-FAMILY: arial, helvetica, lucida-sans, sans-serif;
FONT-SIZE: 18pt;
FONT-STYLE: normal;
FONT-VARIANT: normal;
}
body
{
font-family: arial;
FONT-SIZE: 14px;
}
and if overrider.css is having a style like,
body
{
font-family: Calibri;
color: #3E83F1;
}
Then final CSS(output.css) generated should be,
h1 {
color: white;
background: teal;
FONT-FAMILY: arial, helvetica, lucida-sans, sans-serif;
FONT-SIZE: 18pt;
FONT-STYLE: normal;
FONT-VARIANT: normal;
}
body
{
font-family: Calibri;
FONT-SIZE: 14px;
color: #3E83F1;
}
Here, since style for body in override.css have font-family, it replaces the font-family property in original CSS and since the color is a new property which is not present in ($file) which is original CSS file, so it should add the property to original CSS file. So how to achieve this in PHP, since i don't have any idea on parsing CSS. Any idea on this would be greatly appreciated.
Please note that i need to generate a new CSS file by giving the input as file1($file) and file2(override.css) and we need to genrate output.css with the styles overridden.
Thanks in advance.
There are a few CSS parsers available (google "php css parser"), like this one that I didn't try, but seems interesting. But personally I'd do the parsing myself - following that kind of pseudo-PHP algorithm
read all the files into one string Str, with all "\n", "\r" and "\t" replaced by a space (to make parsing (a bit) easier)
then, function to process (selector => rules)
func deal with selectors and rules:
rules = array()
do {
S = string from current pos to next `{` excluded (selectors)
R = string from '{' to next '}' (rules)
r = explode(';', R)
lr = make array of rules from r trimmed elements
s = explode (',', S)
ls = make array of [selector => lr]
// same sel: latest rule overwrite existing, added if not exist
merge ls into rules
} while (not '}' and not '#' and not EOF); // the '}' would be the closing media
return rules
Main function to deal with medias, and then call the above function
medias = array();
func deal with Str
do {
if (first non blank char is #) {
media = array of listed medias
eat fist '{'
}
else {
media = array ('GLOBAL')
}
selectorsrules = deal with selectors and rules(rest of Str)
foreach (media as m) {
merge in medias(m) selectorsrules, same procedure as above
}
} while (not EOF);
Interesting project but I don't have the time to implement it fully. Result is in medias array.
if you want font-family: arial; to be apply then add it as font-family: arial !important;
you do not need to worry about merging them because browser will automatically add color to body tag from second css of color found in first css then it will overwrite it with second css.
You have 2 choices:
The simple way is to change your css files and add !important where it is important to be there. It is correct to have for example "body" in css more than 1 time. And whenever you a style to be overridden leave it. Ofcourse this kind of approach is mostly manual. You have to know where it will be overwritten and where not.
The second approach requires string parsing, regular expressions, and i mean you should know your way around the string ad how to parse them. You should get each of the files content, save them to a string, and you should compare them using regex to if a tag is exists in both, then merge the tags content. This way is easy to say but hard to implement.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Parse a CSS file with PHP
I want to be able to search through a css file find the class or id and bring back its style.
IE.. css file layout
body {
margin:0;
padding:0;
}
span {
margin:0;
padding:0;
} #input { font:12px arial; }
a { color:#0000FF;
text-decoration:none;
}
.logout a:hover { text-decoration:underline; }
and i want to find say the id " #input " in the file and it to bring back the style so.
font:12px arial;
or
#input{ font:12px arial; }
and ovioulsly if there is more bring that back to but to keep this small like.
i tried my self but no luck as im not so good in regular expressions.
if(file_exists("css/style.css") && filesize("css/style.css") > 0){
$filesize = filesize("css/style.css");
$handle = fopen("css/style.css", "r");
$contents = fread($handle, $filesize);
fclose($handle);
}
preg_match('#^(?:#input{)?([^}]+)#i', $contents, $matches);
echo "style: {$matches[0]} <br>";
print_r($matches2);
please help.
Your specific regex fails for a couple of reasons:
#^(?:#input{)?([^}]+)#i
The ^ marker means start of subject. Since your #input { CSS selector is not at the start of the regex, this would always fail.
Secondly you are marking it optional with (...)?. So the regex will ignore looking for #input anyway.
Then you also don't have the { escaped.
And your actual CSS contains a space between #input and { which your regex does not account for.
More correct would be:
#(?:#input\s*\{)([^}]+)#i
See also Open source RegexBuddy alternatives and Online regex testing for some helpful tools, or RegExp.info for a nicer tutorial.
When I used a module i have some problems. Some white space appeared
$content='';
$vocab = vuagame_getVocByName("the_loai_game");
$terms = taxonomy_get_tree($vocab->vid);
foreach ($terms as $term) {
$content.="<div class='contentBox'>";
$content.="<div class='contentBoxHeader'>";
$content.="<a href='/category/".$term->tid."'>".$term->name."</a>";
$content.="</div>";
$content.="<table class='gameTable' cellspacing='0' cellpadding='5'>";
$content.= views_embed_view('home', 'page', $term->tid);
$content.="</table>";
$content.="<div class='seemorelink'><a href='/category/".$term->tid."'>Xem thêm</a></div>";
$content.="</div><br/>";
}
return $content;
and here's the result with inspect element :
<div class="contentBox"><div class="contentBoxHeader">18+</div>
"
"
<table class="gameTable" cellspacing="0" cellpadding="5"><tbody><tr>
Does anyone can find out my trouble? Thanks a lot!
first, br tag is not a standard now.
debug it like this,
1)try the html without the loop first. or break the loop after its first cycle.
if white space still exits,
replace a href tag with
"<a href='lorum.html'>ispum </a>"
and see if its gone.. there might be a chance of irregular tag open/close
if still it exists,
replace all data objects with just dummy text, if might have some faulty data.
What do you mean by "some white space appeared"? You get something displayed, but there is white space underneath it?
If that's the case, you could try with adding "clearfix" class to your output and adding this in your css file:
.clearfix:after {
content: "";
clear: both;
font-size: 0;
display: block;
height: 0;
visibility: hidden;
}
And yes, this seems unecessary, that could cause the problem too.
It's seems to be a encoding issue. Try to make sure you're saving your .tpl files with encode UTF-8
This should fix weird spaces.