Category: PHP

cygwin lighttpd php mysql, the perfect dev setup for windows

Since a few months, after more than 10 years on Linux, I switched my main OS to win7 (I’ll write something about this switch in a different post) and I’m quite happy with it, if there’s a thing I wasn’t happy about (’till today) was my development servers environment, I had Zend Server + MySQL, both as windows services, eating my pc’s resources also if I was not using it to work. Another thing was that I only work on Linux production servers and having those “c:\” paths just felt wrong.

I already used the great cygwin for a bunch of things, most of all rsync backups, so I decided to try removing Zend Server + MySQL for windows and move those services inside the cygwin environment, so I can start them with a bash script whenever I need them and I’m finally free from the “c:\” paths.

The problem was that there’s no documentation at all about this kind of installation, so:

  • no problems with MySQL, just install the packages, run “mysql_install_db” just the first time, and then call “mysqld_safe &” to start it when you need it
  • I couldn’t make apache2 work so I decided to use lighttpd
  • Configure and install all PHP packages you need using the cygwin ports project cause the main cygwin repository doesn’t have them

I ran into a few troubles configuring PHP for lighttpd, so here you have my /etc/lighttpd/lighttpd.conf:


# lighttpd configuration file
#
# use it as a base for lighttpd 1.0.0 and above
#
# $Id: lighttpd.conf,v 1.7 2004/11/03 22:26:05 weigon Exp $

############ Options you really have to take care of ####################

## modules to load
# at least mod_access and mod_accesslog should be loaded
# all other module should only be loaded if really neccesary
# - saves some time
# - saves memory
server.modules = (
 "mod_rewrite",
# "mod_redirect",
# "mod_alias",
 "mod_access",
# "mod_cml",
# "mod_trigger_b4_dl",
# "mod_auth",
# "mod_status",
# "mod_setenv",
# "mod_fastcgi",
# "mod_proxy",
# "mod_simple_vhost",
# "mod_evhost",
# "mod_userdir",
 "mod_cgi",
# "mod_compress",
# "mod_ssi",
# "mod_usertrack",
# "mod_expire",
# "mod_secdownload",
# "mod_rrdtool",
 "mod_accesslog" )

## a static document-root, for virtual-hosting take look at the
## server.virtual-* options
server.document-root = "/srv/www/htdocs/"

## where to send error-messages to
server.errorlog = "/var/log/lighttpd/error.log"

# files to check for if .../ is requested
index-file.names = ( "index.php", "index.html",
 "index.htm", "default.htm" )

## set the event-handler (read the performance section in the manual)
# server.event-handler = "freebsd-kqueue" # needed on OS X

# mimetype mapping
mimetype.assign = (
 ".pdf" => "application/pdf",
 ".sig" => "application/pgp-signature",
 ".spl" => "application/futuresplash",
 ".class" => "application/octet-stream",
 ".ps" => "application/postscript",
 ".torrent" => "application/x-bittorrent",
 ".dvi" => "application/x-dvi",
 ".gz" => "application/x-gzip",
 ".pac" => "application/x-ns-proxy-autoconfig",
 ".swf" => "application/x-shockwave-flash",
 ".tar.gz" => "application/x-tgz",
 ".tgz" => "application/x-tgz",
 ".tar" => "application/x-tar",
 ".zip" => "application/zip",
 ".mp3" => "audio/mpeg",
 ".m3u" => "audio/x-mpegurl",
 ".wma" => "audio/x-ms-wma",
 ".wax" => "audio/x-ms-wax",
 ".ogg" => "application/ogg",
 ".wav" => "audio/x-wav",
 ".gif" => "image/gif",
 ".jar" => "application/x-java-archive",
 ".jpg" => "image/jpeg",
 ".jpeg" => "image/jpeg",
 ".png" => "image/png",
 ".xbm" => "image/x-xbitmap",
 ".xpm" => "image/x-xpixmap",
 ".xwd" => "image/x-xwindowdump",
 ".css" => "text/css",
 ".html" => "text/html",
 ".htm" => "text/html",
 ".js" => "text/javascript",
 ".asc" => "text/plain",
 ".c" => "text/plain",
 ".cpp" => "text/plain",
 ".log" => "text/plain",
 ".conf" => "text/plain",
 ".text" => "text/plain",
 ".txt" => "text/plain",
 ".dtd" => "text/xml",
 ".xml" => "text/xml",
 ".mpeg" => "video/mpeg",
 ".mpg" => "video/mpeg",
 ".mov" => "video/quicktime",
 ".qt" => "video/quicktime",
 ".avi" => "video/x-msvideo",
 ".asf" => "video/x-ms-asf",
 ".asx" => "video/x-ms-asf",
 ".wmv" => "video/x-ms-wmv",
 ".bz2" => "application/x-bzip",
 ".tbz" => "application/x-bzip-compressed-tar",
 ".tar.bz2" => "application/x-bzip-compressed-tar",
 # default mime type
 "" => "application/octet-stream",
 )

# Use the "Content-Type" extended attribute to obtain mime type if possible
#mimetype.use-xattr = "enable"
## send a different Server: header
## be nice and keep it at lighttpd
# server.tag = "lighttpd"

#### accesslog module
accesslog.filename = "/var/log/lighttpd/access.log"

## deny access the file-extensions
#
# ~ is for backupfiles from vi, emacs, joe, ...
# .inc is often used for code includes which should in general not be part
# of the document-root
url.access-deny = ( "~", ".inc" )

$HTTP["url"] =~ "\.pdf$" {
 server.range-requests = "disable"
}

##
# which extensions should not be handle via static-file transfer
#
# .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )

###### virtual hosts

##
## If you want name-based virtual hosting add the next three settings and load
## mod_simple_vhost
##
## document-root =
## virtual-server-root + virtual-server-default-host + virtual-server-docroot
## or
## virtual-server-root + http-host + virtual-server-docroot
##
#simple-vhost.server-root = "/srv/www/vhosts/"
#simple-vhost.default-host = "www.example.org"
#simple-vhost.document-root = "/htdocs/"

## virtual directory listings

dir-listing.activate = "enable"
## select encoding for directory listings
dir-listing.encoding = "utf-8"

#

# chroot() to directory (default: no chroot() )
#server.chroot = "/"

## change uid to <uid> (default: don't care)
#server.username = "wwwrun"

## change uid to <uid> (default: don't care)
#server.groupname = "wwwrun"

cgi.assign = (".php"=>"/usr/bin/php-cgi")

Now that you’ve the right lighttpd config file, just start lighttpd with “/usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf &” and you’re done, the perfect PHP dev environment on windows is yours.

Share/bookmark:
Digg StumbleUpon Facebook Reddit Mixx del.icio.us Google Live NewsVine

Magento and .git directory hiding

If you’re deploying a Magento website using git you surely do not want the .git directory to be visible from the browser, so just put this line in your .htaccess:

RewriteRule .*\.git/.* - [F]

right after:

RewriteEngine on

let’s have a nice day now :)

Share/bookmark:
Digg StumbleUpon Facebook Reddit Mixx del.icio.us Google Live NewsVine

Magento reindexer issue and very bad fix

On a big website, while Magento (this was a Professional 1.10.something) is reindexing (work done by a cron job) every page (viewed from the browser) seems to try to reindex the products it shows, that ends in a duplicate key error on an index temporary table (catalog_product_index_price_tmp), I needed a rough and quickfix I opened app\code\core\Mage\Index\Model\Resource\Helper\Abstract.php and added a try/catch to the insertFromSelect method, making it become:

public function insertFromSelect($select, $destTable, array $columns, $readToIndex = true)
    {
		try {
        if ($readToIndex) {
            $from   = $this->_getWriteAdapter();
            $to     = $this->_getIndexAdapter();
        } else {
            $from   = $this->_getIndexAdapter();
            $to     = $this->_getWriteAdapter();
        }

        if ($this->useDisableKeys()) {
            $to->disableTableKeys($destTable);
        }
        if ($from === $to) {
            $query = $select->insertFromSelect($destTable, $columns);
            $to->query($query);
        } else {
            $stmt = $from->query($select);
            $data = array();
            $counter = 0;
            while ($row = $stmt->fetch(PDO::FETCH_NUM)) {
                $data[] = $row;
                $counter++;
                if ($counter>2000) {
                    $to->insertArray($destTable, $columns, $data);
                    $data = array();
                    $counter = 0;
                }
            }
            if (!empty($data)) {
                $to->insertArray($destTable, $columns, $data);
            }
        }
        if ($this->useDisableKeys()) {
            $to->enableTableKeys($destTable);
        }
		} catch (Exception $e) {}
        return $this;
    }

It is bad but now it’s working, oh god.

Share/bookmark:
Digg StumbleUpon Facebook Reddit Mixx del.icio.us Google Live NewsVine

PHP 5.4, Zend Framework 2, Zend Studio 9

A lot of things are moving in the PHP ecosystem, but it seems to me that those things are going a little bit out of control and I’ve a few philosophical questions…

PHP 5.4

Hey great, we’ve traits and a built-in web server. Hemmm what about the damn UTF8 support? It’s 2012 and still a substr can split a character in half! And I don’t think the whole world is still using ANSI or ISO-8859, everyone now uses UTF8 and working with UTF8 inside PHP is always a pain. Traits and that web server were really more important?

At least we’ve a useful new feature: array dereferencing, something python had since maybe 10 years (along with the utf8 support…). I’m not telling python is better otherwise I won’t be using 99% PHP since 12 years.

Zend Framework 2

I hated pretty much every MVC framework out there but ZF is really much more than just an MVC framework (also if not so many people can see that). I used its MVC only a little so I won’t talk about that but I loved the entire ZF1 project, it was just perfect, really easy to use, simple code so the documentation is something useless and it was also really easy to include a single element in your project. That was great and it’s ended.

ZF2 is surely a great software but the devs seems not to remember that PHP was born with simplicity in mind and ZF2 is millions light years beyond simplicity:

  • you won’t ever be able to use a single component (let’s say you just want to use zend\db) without having to deal with the autoloader
  • today we’ve ZF2 beta3, I always knew that “beta” meant ”API freeze” and instead from beta2 to beta3 something like EVERYTHING changed! I was writing some code using Zend\Db and I had to throw it all in the garbage because Zend\Db was completely rewritten for beta3. This make me think of a pre-alpha, not a beta.
  • ZF2 is a complete rewrite of ZF1 and thus I bet it will be a hell of bugs for a long long time, in fact I just started using it in real life and I found 2 blocking bugs (blocking for me obvious), one about Zend\Db (unable not to quote values) and one about Zend\Locale (unable to get territory list), both were perfectly working in ZF1
  • ZF bug tracker is like the most silent western movie, you can’t ever hear a voice, that’s just a bit disappointing
  • ZF2 code is so complex that it’s nearly impossible to go through it just to find how a method works so if there won’t be a really great documentation it will really end in a developing pain

Keeping in mind all of the above, would you go back to ZF1 for your new project knowing that in 18 months you’ll have to completely rewrite everything ’cause ZF1 will be out of support? The answer is no but we’re stuck in a limbo.

And a question for the developers, do you really think that pushing things so high about complexity is a good thing for the success of ZF? I’m not so sure about that. ZF1 could be used by every junior dev out there, I wouldn’t say that for ZF2.

Zend Studio 9

At the moment Zend Studio is IHMO the most updated (9.0.2 already supports PHP 5.4) and feature rich PHP IDE but I still can’t understand if the devs are actually using it, just a few examples:

  • Most of the time I’m on ZS it compiles, indexes and refreshes the workspace, completely eating my cpu and sometimes preventing me from saving a file until the indexing process is finished.
  • GIT is widely used now that really big PHP projects are all over the world (anyone said Magento?) but the GIT plugin for ZS is incomplete and caused me some big problem in the testing phase. One feature missing is the support for GIT submodule, but there is not a single project I’ve ever worked on that didn’t have submodule => I’ve to use an external GIT client and this really bothers me

Probably those problems are Eclipse’s problems but that’s not the point.

Conclusions

It’s a long way to the top…

Share/bookmark:
Digg StumbleUpon Facebook Reddit Mixx del.icio.us Google Live NewsVine

Programmatically delete all Magento attribute sets

You’re developing a Magento importer and you want to clean your DB after some tests? Here you have:

require_once dirname(__FILE__) . '/app/Mage.php';
Mage::app()->setCurrentStore(Mage::getModel('core/store')->load(Mage_Core_Model_App::ADMIN_STORE_ID));

$resource = Mage::getSingleton('core/resource');
$db_read = $resource->getConnection('core_read');

$attribute_sets = $db_read->fetchCol("SELECT attribute_set_id FROM " . $resource->getTableName("eav_attribute_set") . " WHERE attribute_set_id<> 4 AND entity_type_id=4");
foreach ($attribute_sets as $attribute_set_id) {
	try {
		Mage::getModel("eav/entity_attribute_set")->load($attribute_set_id)->delete();
	} catch (Exception $e) {
		echo $e->getMessage() . "\n";
	}
}

Note that only products’ attribute sets are deleted, those are often generated automatically during imports.

Share/bookmark:
Digg StumbleUpon Facebook Reddit Mixx del.icio.us Google Live NewsVine

Programmatically delete all Magento categories

You’re developing a Magento importer and you want to clean your DB after some tests? Here you have:

require_once dirname(__FILE__) . '/app/Mage.php';
Mage::app()->setCurrentStore(Mage::getModel('core/store')->load(Mage_Core_Model_App::ADMIN_STORE_ID));

$resource = Mage::getSingleton('core/resource');
$db_read = $resource->getConnection('core_read');

$categories = $db_read->fetchCol("SELECT entity_id FROM " . $resource->getTableName("catalog_category_entity") . " WHERE entity_id>1 ORDER BY entity_id DESC");
foreach ($categories as $category_id) {
	try {
		Mage::getModel("catalog/category")->load($category_id)->delete();
	} catch (Exception $e) {
		echo $e->getMessage() . "\n";
	}
}
Share/bookmark:
Digg StumbleUpon Facebook Reddit Mixx del.icio.us Google Live NewsVine

Create a custom category attribute in Magento

I’ve run into a few posts about this thing but none of them was working out of the box so I took all the info and glued them together so…

if you’ve to create a custom attribute for a Magento category but you don’t have a module (and its installer script) simply create a php file in the project’s root with this code:

require_once('app/Mage.php');
Mage::app()->setCurrentStore(Mage::getModel('core/store')->load(Mage_Core_Model_App::ADMIN_STORE_ID));
$installer = new Mage_Sales_Model_Mysql4_Setup;
$attribute  = array(
	'type' => 'int',
	'label'=> 'Your attribute label',
	'input' => 'text',
	'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL,
	'visible' => true,
	'required' => false,
	'user_defined' => true,
	'default' => "",
	'group' => "General Information"
);
$installer->addAttribute('catalog_category', 'your_attribute_code', $attribute);
$installer->endSetup();

This attribute is a “text”, more info may come in comments if you need.

Share/bookmark:
Digg StumbleUpon Facebook Reddit Mixx del.icio.us Google Live NewsVine

Magento Professional customer’s password hashing

So you want to import customer accounts but you’ve to generate the password hash? here you’ve the algorithm used by Magento Professional (which is different from Magento Community, but you can easily find that one in the net):

$passhash = hash("sha256", $salt . $password) . ":$salt";

By the way, for the community it’s

$passhash = md5($salt . $password) . ":$salt";
Share/bookmark:
Digg StumbleUpon Facebook Reddit Mixx del.icio.us Google Live NewsVine

Create a custom order attribute in Magento

I’ve run into a few posts about this thing but none of them was working out of the box so I took all the info and glued them together so…

if you’ve to create a custom attribute for a Magento order but you don’t have a module (and its installer script) simply create a php file in the project’s root with this code:

require_once('app/Mage.php');
Mage::app()->setCurrentStore(Mage::getModel('core/store')->load(Mage_Core_Model_App::ADMIN_STORE_ID));
$installer = new Mage_Sales_Model_Mysql4_Setup;
$attribute  = array(
        'type'          => 'text',
        'backend_type'  => 'text',
        'frontend_input' => 'text',
        'is_user_defined' => true,
        'label'         => 'Your attribute label',
        'visible'       => true,
        'required'      => false,
        'user_defined'  => false,
        'searchable'    => false,
        'filterable'    => false,
        'comparable'    => false,
        'default'       => ''
);
$installer->addAttribute('order', 'your_attribute_code', $attribute);
$installer->endSetup();

This attribute is a “text” and it’s made not to be visible but just to use it as a container for some data you may need in your custom development, more info may come in comments if you need.

Share/bookmark:
Digg StumbleUpon Facebook Reddit Mixx del.icio.us Google Live NewsVine

P4A 3.8.4 is out!

Another maintenance release for our beloved project, here you’ve the changelog:

- “meta viewport” tag was added to mask’s HTML for a better mobile rendering
- P4A::getMetaViewport() and setMetaViewport() methods were added
- p4a_center_elements() javascript funcion now removes marginLeft/paddingLeft
from the first visible column of the first P4A_Frame widget
- a bug with P4A_Frame and multiple CSS classes was solved
- P4A_Table’s arrow symbols were because the past ones weren’t shown on mobile
devices
- an ob_clean() call was added to P4A_Thumbnail_Generator::outputThumbnail()
method
- Zend Framework was updated to 1.11.10
- P4A_Table’s image cols do not throw exception anymore if the image
does not exist
- P4A_Field_loadSelectByArray helper now supports a second parameter to specify
the array’s primary key field name

Download P4A 3.8.4

Share/bookmark:
Digg StumbleUpon Facebook Reddit Mixx del.icio.us Google Live NewsVine