Tag: php
JSLint php class
by z3n on Jul.07, 2010, under Coding, Tips & Hints
Problem:
How to lint check a javascript using php?
Solution:
// (c) z3n - R1V1@100707 - www.overflow.biz - rodrigo.orph@gmail.com
// Based on the original by Matthias Miller (http://www.JavaScriptLint.com/)
class JSLEngine {
private $_binarypath; // jlint exec
private $_confpath; // config path
private $fn; // temp filename (not used outside class)
private $r; // jlint output
private $has_errors=0; // error flag
public function __construct($binarypath="", $confpath="") {
// default paths
base_defines(array(
"jslint_binary_path" => _fn_fix(dirname(dirname(dirname(__FILE__)))."/3rd/jsl-0.3.0/jsl.exe"),
"jslint_conf_path" => _fn_fix(dirname(dirname(dirname(__FILE__)))."/3rd/jsl-0.3.0/jsl.default.conf")
));
// startup
$this->_binarypath = $binarypath == "" ? jslint_binary_path : $binarypath;
$this->_confpath = $confpath == "" ? jslint_conf_path : $confpath;
}
public function __destruct() {
if ($this->fn != null && file_exists($this->fn))
unlink($this->fn);
}
/* returns error on failure; returns true on success */
public function Lint($code) {
if (!$this->_launchLintBinary($code, $output))
die('The JavaScript Lint online service is currently unavailable.');
// store lint
$this->r=$output;
$output=explode("\n",$output); // break lines
$x=$output[count($output)-2]; // X error(s), X warning(s) (total lines -2)
$x=trim(substr($x,0,strpos($x," ")));
if ($x > 0) { // has errors
$this->has_errors=1;
return false;
} else { // clean
$this->has_errors=0;
return true;
}
}
/* assumes path and that SERVER_SOFTWARE env is set */
private function _launchLintBinary($input, &$output) {
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w")
);
$this->fn=_fn_fix(dirname(__FILE__).'/tmp.js');
file_put_contents($this->fn,$input);
/* launch process */
$path = PHP_OS == "WINNT" ? $this->_binarypath : escapeshellcmd($this->_binarypath);
$path.= ' --nologo --conf '.escapeshellarg($this->_confpath).' --process '.escapeshellarg($this->fn);
$process = proc_open($path, $descriptorspec, $pipes);
if (!is_resource($process))
return false;
$output = '';
while (!feof($pipes[1]))
$output .= fgets($pipes[1], 1024);
fclose($pipes[1]);
fclose($pipes[2]);
// It is important that you close any pipes before calling
// proc_close in order to avoid a deadlock
$return_value = proc_close($process);
return true;
}
public function output() {
return $this->r;
}
}
Helper functions:
function _fn_fix($fn,$force="") { // v1.02
if (strpos($fn,"://") === false) {
if ((PHP_OS == "WINNT" && $force == "") || $force == "WINNT")
$fn=str_replace("/","\\",$fn);
else
$fn=str_replace("\\","/",$fn);
}
if (strpos($fn,":/") !== false && strpos($fn,"://") === false)
$fn=substr($fn,2);
return $fn;
}
function base_defines($x) { // define default
foreach ($x as $k => $v) {
if (!defined($k)) {
define($k,$v);
}
}
}
Usage:
$lint=new JSLEngine();
if (!$lint->Lint($js)) {
echo "bad js code! full output:\n";
echo $lint->output();
}
Required:
jslint binaries from http://www.JavaScriptLint.com/
Make sure you set the default path on the __construct, so you don’t need to keep setting it on every call.
CSS Packer Reloaded
by z3n on Jun.21, 2010, under Coding, Tips & Hints
Sometime ago i posted a simple css packer which basically wipes out the whitespaces, new lines and so on. The javascript packer was also included. Although none of those were 100% my creations i’ve improved the way it works by adding a couple of things:
- Caching based on timestamp on files (aka automatic cache expiry on update)
- Packing multiple files into a single one (ex. if you have 100s of jquery extensions you don’t need to pack them individually, packing everything into a single file is faster and smaller)
- Flexible layout for packing IE specific files
Source:
function css_specifics($fn) {
global $_GET;
if (isset($_GET['q'])) {
return str_replace(".css",".".trim(str_replace(array("..","/"),"",$_GET['q'])).".css",$fn);
} else {
return $fn;
}
}
base_defines(array(
"css_packed" => _is_sadm ? "packed-adm.css" : "packed.css", // css worked file output
"css_include_path" => "css/", // css base include path
"css_cache_path" => "css/cache/", // css cache path
"css_compression" => -1, // css compression method, -1 = disabled
"css_copyright" => "/* (c) z3n - www.overflow.biz - rodrigo.orph@gmail.com */\n\n", // css copyright notice, will appear on packed verisons
"css_debug" => 0,
"css_gzip" => 1
));
$css="";
$_use_cache=0;
$css_packed=css_specifics(css_packed);
$l=(file_exists(css_cache_path.$css_packed)) ? filemtime(css_cache_path.$css_packed) : 0;
if (isset($css_includes) && (!empty($css_includes))) {
if ($l > 0) { // check last mod first
$_use_cache=1;
if (css_debug)
echo "collected includes: ".var_export($css_includes,true)."\n";
foreach ($css_includes as $v) {
$fn=_fn_fix($v{0} == "/" || strpos($v,":\\") !== false || $v{0} == "\\" ? $v : css_include_path.$v);
if (css_debug)
echo "checking lastmod for: ".$v."\n";
if (foo((substr($v,0,5) == "http:") ?
getlastmod($fn)
:
filemtime($fn)
) > $l) {
$_use_cache=0;
break;
}
}
}
if (!$_use_cache) { // re/pack
if (css_debug)
echo "building new cache\n";
// load all js into one
foreach ($css_includes as $v)
$css.=substr($v,0,5) == "http:" ?
file_get_contents($v)."\n"
:
_fs($v{0} == "/" || strpos($v,":\\") !== false || $v{0} == "\\" ? $v : css_include_path.$v)."\n"
;
// @Packager.RemoveLine -> nothing
if (css_debug)
echo "replacing..\n";
// perform basic replaces
$css=str_replace(array(
"##SITE_FULL_URI##"
),array(
site_full_uri
),$css);
// compress
switch (css_compression) {
case "0": // css tidy - http://csstidy.sourceforge.net/
require_once("classes/csstidy/class.csstidy.php");
$x=new csstidy();
$x->set_cfg('preserve_css',true);
$x->set_cfg('remove_last_;',true);
$x->set_cfg('merge_selectors',1);
$x->set_cfg('optimise_shorthands',1);
$x->set_cfg('silent',true);
$x->set_cfg('compress_colors',true);
$x->set_cfg('sort_selectors',false);
$x->set_cfg('sort_properties',false);
$x->set_cfg('discard_invalid_properties',false);
$x->set_cfg('timestamp',false);
$x->load_template("highest_compression");
$x->parse(strip_comments($css));
$css=$x->print->plain();
break;
case "1": // minify - http://www.lateralcode.com/css-minifier/
$css = preg_replace( '#\s+#', ' ', $css );
$css = preg_replace( '#/\*.*?\*/#s', '', $css );
$css = str_replace( '; ', ';', $css );
$css = str_replace( ': ', ':', $css );
$css = str_replace( ' {', '{', $css );
$css = str_replace( '{ ', '{', $css );
$css = str_replace( ', ', ',', $css );
$css = str_replace( '} ', '}', $css );
$css = str_replace( ';}', '}', $css );
break;
default: // no compression/invalid
// nothing!
break;
}
$css=css_copyright.$css; // add copyright notice
if (css_debug)
echo "writing cache..\n";
_fw(css_cache_path.$css_packed,$css,"w"); // save cache
} else { // use cache
if (css_debug)
echo "loading from cache\n";
$css=_fs(css_cache_path.$css_packed);
}
if (css_debug)
echo "output\n - - - \n".$css;
_opt_headers(gmdate(_dt_lm,filemtime(css_cache_path.$css_packed))." GMT"); // check/send 304
header('Content-Type: text/css');
if (css_gzip) // compress
ob_start("ob_gzhandler");
print $css; // final output
}
die;
Usage:
Configure files to be added by:
$css_includes=array(
"file1.css",
"file2.css",
"fileN.css",
"http://www.remote.com/style.css"
);
Do the link by:
<link rel="stylesheet" href="/lib/css.php" type="text/css"> <!--[if IE]><link rel="stylesheet" href="/lib/css.php?q=ie" type="text/css"><![endif]-->
Assuming you have the source into /lib/css.php and, on this case, you have an exception for all IEs.
Notes:
css_include_path = source of css (uncompressed, commented)
css_cache_path = path to save the compressed css
css_compression = method of compression , -1 no compression , 0 = css tidy, 1 = minify
css_debug = enables step by step debug
css_gzip = forces php to output as gzip
Required:
You may also need some functions of my framework, i will post them when i compile a decent common file.
Botnet ddos and mail server hammering
by z3n on Jun.05, 2010, under Coding, Linux Happyness
Problem:
So i noticed a real slow down on my server recently, nothing new was installed, no new sites launched. Investigating it a little further i saw like +100 sendmail processes running in background, WTF!
Solution:
Looking at huge maillogs, i found that there’s a botnet hammering my mail server, trying to authenticate with random strings and send unauthenticated emails, as if my server were configured as a promiscuous email server.
So, i had to put in practice my never used before iptables config skills.
First i added a rule to avoid global hammering:
-N SMTP-BLOCK -A SMTP-BLOCK -s 127.0.0.1 -j ACCEPT -A SMTP-BLOCK -s ##YOUR_SERVER_IPS## -j ACCEPT -A SMTP-BLOCK -m limit --limit 1/m --limit-burst 3 -j LOG --log-level notice --log-prefix "iptables SMTP-BLOCK " -A SMTP-BLOCK -m recent --name SMTPBLOCK --set -j DROP -A INPUT -p tcp --dport 25 -m state --state NEW -m recent --name SMTPBLOCK --rcheck --seconds 360 -j SMTP-BLOCK -A INPUT -p tcp --dport 25 -m state --state NEW -m recent --name SMTP --set -A INPUT -p tcp --dport 25 -m state --state NEW -m recent --name SMTP --rcheck --seconds 60 --hitcount 10 -j SMTP-BLOCK -A INPUT -p tcp --dport 25 -m state --state NEW -j ACCEPT
This will avoid hammering on port 25, SMTP, make sure you add your server’s ip here, otherwise it might slowdown your own server from sending emails out. This will ban users for 60 seconds if they send more than 3 packets a minute on port 25, pretty much enough what you need to do.
Now the bot net will still hammering, since they have MANY different ips , 800 so far, they can still hammering and eating up your server resources and bandwidth, since they will not stop i wrote a script to analyze maillogs and ban by ip:
// (c) z3n - R1V1@100605 - www.overflow.biz - rodrigo.orph@gmail.com
if (!isset($argv[2])) die("Usage: ".$_SERVER['PHP_SELF']." <input file> <ip tables>");
function _gii($s,$s_1,$s_2,$st=1) { // what, start string, end string, strip tags bool
if ((stripos($s,$s_1) !== false) && (stripos($s,$s_2) !== false)) {
$p=strlen($s_1)+stripos($s,$s_1);
return ($st == 0) ? substr($s,$p,stripos($s,$s_2,$p)-$p) : strip_tags(substr($s,$p,stripos($s,$s_2,$p)-$p));
} else {
return "";
}
}
function _r($x) { echo $x."\n"; }
$ips=array();
$handle=fopen($argv[1],"r");
while (!feof($handle)) {
$buf=fgets($handle,4096);
$x=explode("\n",$buf);
foreach ($x as $v) {
if (strpos($v,"Relaying denied") !== false) {
$ip=_gii($v," [","]",0);
if (!in_array($ip,$ips))
$ips[]=$ip;
}
}
}
for ($r="",$i=0,$j=count($ips),_r("Found: ".$j." entries, building ban list..."),$ip_tables=file_get_contents($argv[2]);$i < $j;$i++)
if (strpos($ips[$i],$ip_tables) === false)
$r.="-A INPUT -s ".$ips[$i]." -j REJECT\n";
if (!empty($r)) {
file_put_contents("maillog.ban",$r);
_r("Wrote maillog.ban");
} else {
_r("No new entries found");
}
..not so good on preg, but it works :P
once you generated the maillog.ban file just paste it on the iptables.
Besides that you may want to tune up your MTA child limit in order to avoid server overloading.
You may also want to keep this:
-A INPUT -p tcp -m tcp --syn -m limit -m state --limit 1/second --limit-burst 5 --state NEW -j ACCEPT -A INPUT -p tcp -m tcp --syn -m state --state NEW -j REJECT
on for a while, it will ban ips based on their sync packet usage, let’s say that our unhappy script kiddie who owns the botnet starts to ping the server instead, this will avoid server from having resources wasted, but may also cause issues with legit clients.
I also found this article that shows how to disable ping echoing straight on kernel just by adding net.ipv4.icmp_echo_ignore_all = 1 at /etc/sysctl.conf.
Custom twitter data fetching in php with cache
by z3n on May.28, 2010, under Coding, Tips & Hints
Problem:
I had to fit the twitter posts (“tweets”) on a site using a special layout, without all the regular twitter crapola
Solution:
There’s this old script that return contents as json, although i could simply add it to the javascript, it would slow down the site, everytime a new page was loaded user would need to fetch tweets all over again, so i did a little php function to fetch it cache and add to the output:
function _ld_twitter($profile=_twitter_profile,$tweets=_twitter_tweets) { // v1.0
// check cache
$hash=md5(md5($profile).md5($tweets));
if (_twitter_cache && file_exists(_twitter_cache_path.$hash.".idx") && file_exists(_twitter_cache_path.$hash) && _fs(_twitter_cache_path.$hash.".idx") > getmicrotime())
return _fs(_twitter_cache_path.$hash);
// build
$x=file_get_contents("http://twitter.com/statuses/user_timeline/".$profile.".json?callback=x&count=".$tweets);
if ($x != "") {
$x=json_decode(substr($x,2,-2));
$b=_fs(templates."twitter".file_ext);$r="";$i=count($x)-1;
foreach ($x as $k => $v)
$r.=str_replace(array(
"##WHO##","##IMG##",
"##WHEN##","##MSG##",
"##DELAY##","##LAST##"
),array(
$v->user->screen_name,$v->user->profile_image_url,
date(_dt2,strtotime($v->created_at)),not_utf8($v->text),
duration(getmicrotime()-strtotime($v->created_at),1,0),$k == $i ? " id='last'" : ""
),$b);
// do cache
if (_twitter_cache) {
_fw(_twitter_cache_path.$hash.".idx",(_twitter_cache_life*60) + getmicrotime(),"w");
_fw(_twitter_cache_path.$hash,$r,"w");
}
// return
return $r;
} else {
return false;
}
}
// defines -- yes not formatted correctly
"_twitter_profile" => "your_tweeter_profile_name", // twitter profile
"_twitter_tweets" => 3, // how many tweets to load
"_twitter_cache_life" => 15, // twitter cache life in minutes
"_twitter_cache_path" => "lib/cache/", // twitter cache path
"_twitter_cache" => 1, // enables / disables twitter cache
"_dt2" => "d M Y",
"templates" => "templates/",
"file_ext" => ".html"
/*
functions (not added here)
those functions are enhanced versions of basic functions, to try this script you can just take :
_fs = file_get_contents
_fw = file_put_contents
duration = a function to return a number of seconds into years, months, days, hours, minutes, seconds notation
*/
If you want to implement it as javascript, you can use the url:
http://twitter.com/statuses/user_timeline/”.$profile.”.json?callback=x&count=”.$tweets
where the callback is the function where the json data will be sent to.
Multi threaded php without EXEC
by z3n on May.20, 2010, under Coding, Tips & Hints
Problem:
How to do a multi threaded php execution without using exec or any other less secure method?
Solution:
function _load_thread() { // v1.0
global $_SESSION,$_POST,$_GET,$_REQUEST,$_COOKIE,$_SERVER;
if (isset($_SESSION)) {
if (isset($_SESSION['is_thread']) && $_SESSION['is_thread']) {
ignore_user_abort(true);
$_SESSION['is_thread']=0;
$x=unserialize($_SESSION['thread']);
$_SESSION['thread']="";
$_POST=$x['POST'];
$_GET=$x['GET'];
$_REQUEST=$x['REQUEST'];
@ $_SERVER=$x['SERVER'];
$_COOKIE=$x['COOKIE'];
define("_is_thread",1);
}
}
}
function _save_thread() { // v1.0
global $_SESSION,$_POST,$_GET,$_REQUEST,$_COOKIE,$_SERVER;
if (isset($_SESSION)) {
$_SESSION['is_thread']=1;
$_SESSION['thread']=serialize(array(
"POST" => $_POST,
"GET" => $_GET,
"REQUEST" => $_REQUEST,
"SERVER" => $_SERVER,
"COOKIE" => $_COOKIE
));
}
}
function _go_thread() { // v1.0
global $_SERVER,$post;
_save_thread();
$post="thread_sessid=".session_id();
@ cget(slash_fix(site_full_uri.$_SERVER['PHP_SELF']),"none",1,0,0,1,0,1);
}
function cget($url,$ref="none",$is_post=0,$is_cookie=0,$headers=0,$follow=1,$verbose=0,$timeout=30) { // v1.05-lite
global $post,$cookie;
$r=$tail="";
$ch=curl_init($url);
curl_setopt($ch,CURLOPT_VERBOSE,$verbose);
curl_setopt($ch,CURLOPT_HEADER,$headers);
curl_setopt($ch,CURLOPT_HTTPHEADER,array("User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)","Accept-Encoding:deflate"));
curl_setopt($ch,CURLOPT_FOLLOWLOCATION,$follow);
curl_setopt($ch,CURLOPT_MAXREDIRS,5);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch,CURLOPT_REFERER,$ref);
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,30); // default 30
curl_setopt($ch,CURLOPT_TIMEOUT,$timeout); // default 30
if ($is_post) {
curl_setopt($ch,CURLOPT_POST,1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$post); // post must be a string, if you set it as a array it will use a non standard multi-part post
$tail.=" ".$post;
unset($post);
}
if ($is_cookie) {
curl_setopt($ch,CURLOPT_COOKIE,$cookie);
$tail.=" ".$cookie;
unset($cookie);
}
$r=curl_exec($ch);
curl_close($ch);
return $r;
}
How it works:
When you want to send a long php job to background, assuming you have this very same job into the same php user is on but delimited by _is_thread define, you just need to call _go_thread(); , it will save ALL vars into the session_id, do a cURL call the PHP_SELF (you need to define site_full_uri as the full address of the request, like http://192.168.1.65/) this cURL have a timeout of 1s on propouse so the caller script is able to send the reply to the user fast, while the real job is done in background.
When MySQL Query Optimizing don’t work
by z3n on May.13, 2010, under Coding, Tips & Hints
Problem:
A supposed plain query like:
select pacote.*, users.id as uid,users.usr, group_concat(content.nm separator '|') as fnm, group_concat(historico.mid separator '|') as fmid from pacote join historico on pacote.id=historico.pid join content on historico.mid=content.id join users on users.id=historico.uid group by historico.pid,pacote.id order by pacote.id desc
takes 15s to run, only because of the order by, assuming that each of the involved tables have at least 100,000 entries and pacote.id is the primary key.
Solution:
I’ve searched a lot but there’s no way to optimize this, limits will not help due the `order by`, having this query un-merged in many others will get worst results, having it without join will get about the same processing time (a bit slower in long run).
So, the rest of options were quite bad:
1. Remove crap rows out the tables
2. Get new hardware?!
Although pacote.added will not match pacote.id 100%, it will match 99.9%, so why not use the bare sorting (without order by mysql will output the rows in primary key order) ?
This would require a little coding to have it right:
So we have the same query as above WITHOUT order by:
select pacote.*, users.id as uid,users.usr, group_concat(content.nm separator '|') as fnm, group_concat(historico.mid separator '|') as fmid from pacote join historico on pacote.id=historico.pid join content on historico.mid=content.id join users on users.id=historico.uid group by historico.pid,pacote.id
(0.1s avg)
now on php:
// some vars
define("_ipp",20); // items per page
$x['page']=0; // current page
// do the query (assuming that $sql is the query above)
$res=mysql_db_query($db,$sql);
// build an array with ALL results
for ($rez=array();$r=mysql_fetch_array($res,MYSQL_ASSOC);$rez[]=$r) { }
// cut the array from the end (note minus signal)
$rez=array_slice($rez,-(intval($x['page']+1)*_ipp),_ipp);
krsort($rez); // invert array, simulating order by's DESC
foreach ($rez as $val) {
// do things
}
(4.25s avg)
So the php solution is far faster than the mysql order by, although it’s not accurate as, it really solved the issue.
UTF8 Conversion Functions
by z3n on Apr.24, 2010, under Coding, Tips & Hints
Problem:
There are many issues when you run across a non utf8 string and even worse a mixed variable, that could or couldn’t be utf8 at same time.
Solution:
Based on my previous postings i’ve improved my utf8 functions taking as referral this great post.
define('_is_utf8_split',5000);
function utf8_encode_array(&$x) {
if (is_array($x)) {
foreach ($x as &$v) // loop through arrays and/or items
$v=utf8_encode_array($v);
return $x;
} else // not array
return !is_utf8($x) ? utf8_encode($x) : $x;
}
function to_utf8($x) { // v1.01
/*
This function will convert a string or an array to utf8.
The input can have mixed encodings.
-- 100424
*/
if (!is_utf8($x)) {
if (is_array($x)) {
foreach ($x as &$v) {
$v=to_utf8($v);
}
} else {
$x=utf8_encode($x);
}
}
return $x;
}
function is_utf8($string) { // v1.03
if (is_array($string)) {
foreach ($string as $v) {
if (is_utf8($string))
return true;
}
return false;
} elseif (strlen($string) > _is_utf8_split) {
// Based on: http://mobile-website.mobi/php-utf8-vs-iso-8859-1-59
for ($s=$i=0,$j=ceil(strlen($string)/_is_utf8_split);$i < $j;$i++,$s+=_is_utf8_split) {
if (!is_utf8(substr($string,$s,_is_utf8_split)))
return false;
}
return true;
} else {
// From http://w3.org/International/questions/qa-forms-utf-8.html
return preg_match('%^(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs', $string);
}
}
function _r_json($x) {
$x=utf8_encode_array($x);
echo json_encode($x);
}
PNG Watermarking images with PHP
by z3n on Apr.07, 2010, under Coding, Tips & Hints
Problem:
How to watermark images with png’s alphablending enabled?
Solution:
function image_overlap($background, $foreground, $overlapX=-1, $overlapY=-1) {
$insertWidth = imagesx($foreground);
$insertHeight = imagesy($foreground);
$imageWidth = imagesx($background);
$imageHeight = imagesy($background);
$overlapX = ($overlapX == -1) ? $imageWidth-$insertWidth-5 : $overlapX;
$overlapY = ($overlapY == -1) ? $imageHeight-$insertHeight-5 : $overlapY;
imagecolortransparent($foreground,imagecolorat($foreground,0,0));
imagecopymerge($background,$foreground,$overlapX,$overlapY,0,0,$insertWidth,$insertHeight,100);
return $background;
}
function watermark($src,$watermark,$src_format,$quality=75) {
if (file_exists($src)) {
_o("Watermarking: ".$src."...",1);
switch ($src_format) {
case "jpg":
$src_img=imagecreatefromjpeg($src);
break;
case "gif":
$src_img=imagecreatefromgif($src);
break;
case "bmp":
$src_img=imagecreatefrombmp($src);
break;
case "png":
$src_img=imagecreatefrompng($src);
break;
default:
die("Unknown format");
}
// prepare source
$src_size=getimagesize($src);
$img=imagecreatetruecolor($src_size[0],$src_size[1]);
imagecopyresampled($img,$src_img,0,0,0,0,$src_size[0],$src_size[1],$src_size[0],$src_size[1]);
@ imagealphablending($img,true);
// load watermark
$watermark_size=getimagesize($watermark);
$watermark_img=imagecreatefrompng($watermark);
@ imagealphablending($wartermark_img,true);
// apply watermark
$img=image_overlap($img,$watermark_img);
imagecopy($img,$watermark_img,0,0,0,0,0,0);
imagedestroy($src_img);
imagedestroy($watermark_img);
switch ($src_format) {
case "jpg":
imagejpeg($img,$src,$quality);
break;
case "gif":
imagegif($img,$src);
break;
case "bmp":
imagebmp($img,$src);
break;
case "png":
imagepng($img,$src,$quality);
break;
}
imagedestroy($img);
} else {
die("Watermark: Source don't exists -- ".$src);
}
}
Note:
For BMPs you will need the functions on this post.
Image Resizing Functions (… again?!)
by z3n on Mar.27, 2010, under Coding, Tips & Hints
Problem:
Oh yeah i posted this before, but now that i finally got wordpress updates i can use a decent syntax highlighter.
The main function img_resizer, is able to resize gif, png, wbmp, bmp and jpgs with proportional or cropping resize, it’s also got a function to eventually FIX the format if the filename extension is not really the same of the format, like a jpg named as file.bmp. BMP function been taken off php man pages, i will not post author’s full email to avoid spam.
Solution:
function dwordize($str) {
$a = ord($str[0]);
$b = ord($str[1]);
$c = ord($str[2]);
return $c*256*256 + $b*256 + $a;
}
function byte3($n) {
return chr($n & 255) . chr(($n >> 8) & 255) . chr(($n >> 16) & 255);
}
function dword($n) {
return pack("V", $n);
}
function word($n) {
return pack("v", $n);
}
function imagebmp(&$img, $filename = false) {
$wid = imagesx($img);
$hei = imagesy($img);
$wid_pad = str_pad('', $wid % 4, "\0");
$size = 54 + ($wid + $wid_pad) * $hei;
//prepare & save header
$header['identifier'] = 'BM';
$header['file_size'] = dword($size);
$header['reserved'] = dword(0);
$header['bitmap_data'] = dword(54);
$header['header_size'] = dword(40);
$header['width'] = dword($wid);
$header['height'] = dword($hei);
$header['planes'] = word(1);
$header['bits_per_pixel'] = word(24);
$header['compression'] = dword(0);
$header['data_size'] = dword(0);
$header['h_resolution'] = dword(0);
$header['v_resolution'] = dword(0);
$header['colors'] = dword(0);
$header['important_colors'] = dword(0);
if ($filename) {
$f = fopen($filename, "wb");
foreach ($header AS $h) {
fwrite($f, $h);
}
// save pixels
for ($y=$hei-1; $y>=0; $y--) {
for ($x=0; $x<$wid; $x++) {
$rgb = imagecolorat($img, $x, $y);
fwrite($f, byte3($rgb));
}
fwrite($f, $wid_pad);
}
fclose($f);
} else {
foreach ($header AS $h)
{
echo $h;
}
//save pixels
for ($y=$hei-1; $y>=0; $y--) {
for ($x=0; $x<$wid; $x++) {
$rgb = imagecolorat($img, $x, $y);
echo byte3($rgb);
}
echo $wid_pad;
}
}
}
function _ckdir($fn) {
if (strpos($fn,"/") !== false) {
$p=substr(substr($fn,0,strrpos($fn,"/")),0,250);
if (!is_dir($p)) {
mkdir($p,755,true);
}
}
}
function imagecreatefrombmp($p_sFile) { // by alexander at http://www.php.net/manual/en/function.imagecreatefromwbmp.php
// Load the image into a string
$file = fopen($p_sFile,"rb");
$read=fread($file,10);
while(!feof($file)&&($read<>""))
$read.=fread($file,1024);
$temp=unpack("H*",$read);
$hex=$temp[1];
$header=substr($hex,0,108);
// Process the header
// Structure: http://www.fastgraph.com/help/bmp_header_format.html
if (substr($header,0,4) == "424d") {
// Cut it in parts of 2 bytes
$header_parts=str_split($header,2);
// Get the width4 bytes
$width=hexdec($header_parts[19].$header_parts[18]);
// Get the height4 bytes
$height=hexdec($header_parts[23].$header_parts[22]);
// Unset the header params
unset($header_parts);
}
// Define starting X and Y
$x=0;
$y=1;
// Create newimage
$image=imagecreatetruecolor($width,$height);
// Grab the body from the image
$body=substr($hex,108);
// Calculate if padding at the end-line is needed
// Divided by two to keep overview.
// 1 byte = 2 HEX-chars
$body_size=(strlen($body)/2);
$header_size=($width*$height);
// Use end-line padding? Only when needed
$usePadding=($body_size>($header_size*3)+4);
// Using a for-loop with index-calculation instaid of str_split to avoid large memory consumption
// Calculate the next DWORD-position in the body
for ($i=0;$i < $body_size;$i+=3) {
// Calculate line-ending and padding
if ($x >= $width) {
// If padding needed, ignore image-padding
// Shift i to the ending of the current 32-bit-block
if ($usePadding)
$i+=$width%4;
// Reset horizontal position
$x=0;
// Raise the height-position (bottom-up)
$y++;
// Reached the image-height? Break the for-loop
if ($y > $height)
break;
}
// Calculation of the RGB-pixel (defined as BGR in image-data)
// Define $i_pos as absolute position in the body
$i_pos=$i*2;
$r=hexdec($body[$i_pos+4].$body[$i_pos+5]);
$g=hexdec($body[$i_pos+2].$body[$i_pos+3]);
$b=hexdec($body[$i_pos].$body[$i_pos+1]);
// Calculate and draw the pixel
$color=imagecolorallocate($image,$r,$g,$b);
imagesetpixel($image,$x,$height-$y,$color);
// Raise the horizontal position
$x++;
}
// Unset the body / free the memory
unset($body);
// Return image-object
return $image;
}
function img_force($src,$quality,$w,$h,$saveas,$nocrop,$force) {
foreach (array("jpg","gif","png") as $x) { // will skip bmp formats
if ($x != $force) {
if (img_resizer($src,$quality,$w,$h,$saveas,$nocrop,$x)) {
return 1;
break;
}
}
}
return 0;
}
function img_resizer($src,$quality,$w,$h,$saveas,$nocrop=0,$force=0) { /* v2.72 with auto crop, no crop, automatic file type detection when avalible (requires exif library) */
if (!is_file($src)) { _o("Source is not a file.");return 0; }
if (!is_readable($src)) { _o("Can't read: ".$src);return 0; }
if ((file_exists($saveas)) && (!is_writeable($saveas))) { _o("Can't write to: ".$saveas);return 0; }
$r=1;
if (function_exists('exif_imagetype')) { // automatic filetype detection
$e=exif_imagetype($src);
if ($e == IMAGETYPE_JPEG) {
if (!$OldImage=@ImageCreateFromJpeg($src)) { $r=0; }
} elseif ($e == IMAGETYPE_GIF) {
if (!$OldImage=@ImageCreateFromGif($src)) { $r=0; }
} elseif ($e == IMAGETYPE_BMP) { // custom
if (!$OldImage=@ImageCreateFrombmp($src)) { $r=0; }
} elseif ($e == IMAGETYPE_WBMP) {
if (!$OldImage=@ImageCreateFromwbmp($src)) { $r=0; }
} elseif ($e == IMAGETYPE_PNG) {
if (!$OldImage=@ImageCreateFromPng($src)) { $r=0; }
} else {
_o("Not a Valid Image! (".$e.") -- ".$src);$r=0;
}
} else { // filename based filetype detection, might be subject to incorrect file extensions, resulting on a error, which when enabled will fall into a try and error `force` mode
$e=($force == "0") ? strtolower(substr($src,strrpos($src,".")+1)) : $force;
if (($e == "jpg") || ($e == "jpeg")) {
if (!$OldImage=@ImageCreateFromJpeg($src)) { $r=0; }
} elseif ($e == "gif") {
if (!$OldImage=@ImageCreateFromGif($src)) { $r=0; }
} elseif ($e == "bmp") { // this is NOT wbmp
if (!$OldImage=@ImageCreateFrombmp($src)) { $r=0; }
} elseif ($e == "png") {
if (!$OldImage=@ImageCreateFromPng($src)) { $r=0; }
} else {
_o("Not a Valid Image! (".$e.") -- ".$src);$r=0;
}
}
if ($r) {
list($width,$height)=getimagesize($src);
// check if ratios match
$_ratio=array($width/$height,$w/$h);
if ($_ratio[0] != $_ratio[1]) { // ratio don't match
if ($nocrop) { // since we can't crop we will need to do image on a different height/width
$hx = (100 / ($width / $w)) * .01;
$hx = @round ($height * $hx);
$wx = (100 / ($height / $h)) * .01;
$wx = @round ($width * $wx);
if ($hx < $h) {
$h = (100 / ($width / $w)) * .01;
$h = @round ($height * $h);
} else {
$w = (100 / ($height / $h)) * .01;
$w = @round ($width * $w);
}
} else { // crop image
// find the right scale to use
$_scale=min((float)($width/$w),(float)($height/$h));
// coords to crop
$cropX=(float)($width-($_scale*$w));
$cropY=(float)($height-($_scale*$h));
// cropped image size
$cropW=(float)($width-$cropX);
$cropH=(float)($height-$cropY);
$crop=ImageCreateTrueColor($cropW,$cropH);
ImageCopy($crop,$OldImage,0,0,(int)($cropX/2),(int)($cropY/2),$cropW,$cropH); // crop the middle part of the image to fit the thumbnail's proportions
}
}
// do the thumbnail
$NewThumb=ImageCreateTrueColor($w,$h);
if (isset($crop)) { // been cropped
ImageCopyResampled($NewThumb,$crop,0,0,0,0,$w,$h,$cropW,$cropH);
ImageDestroy($crop);
} else { // ratio match, regular resize
ImageCopyResampled($NewThumb,$OldImage,0,0,0,0,$w,$h,$width,$height);
}
_ckdir($saveas);
ImageJpeg($NewThumb,$saveas,$quality);
ImageDestroy($NewThumb);
ImageDestroy($OldImage);
} elseif (($force == "0") && (!function_exists('exif_imagetype'))) {
$r=img_force($src,$quality,$w,$h,$saveas,$nocrop,$e);
}
return $r;
}
See how nice it looks with the syntax highligher? :P
Updates:
100407 – Added imagebmp function to save as bmp;
Fatal error: Cannot use object of type stdClass as array (php json_decode)
by z3n on Feb.17, 2010, under Coding, Notes, Tips & Hints
Problem:
$var=json_decode($_POST['something']);
echo $var['value'];
returns error: Fatal error: Cannot use object of type stdClass as array
Solution:
echo $var->value;