IIS7 PHP WSDL issue

If you ever run into a scenario where you need to run a PHP SOAP webservice in an IIS environement, you might encounter the following problem. When generating a client proxy in .NET/WCF this exception is thrown;

The server committed a protocol violation. Section=ResponseHeader Detail=CR must be followed by LF

The problem, as it turns out, is that ASP.NET appends the HTTP X-Powered-By header. This header seems to confuse the proxy generation code. To fix the issue you need to remove this IIS header. Head to your IIS Manager > You website > The folder your PHP webservice is located > Click HTTP Response header. Then remove the X-Powered-By header. Now enjoy generating proxies!

Some evil internet voices claim that you should set useUnsafeHeaderParsing to false in your clients’ application configuration to make .NET ignore the inconsistency in your webservice WSDL output. This however is a workaround, not a solution. So if you run the webservice apply the header fix as described above.

Remove IIS headers

Magento LAMP vs WIMP: Running Magento with IIS and WinCache

Let’s get started by getting IIS up and running with WinCache. Getting your IIS up and running with PHP itself is easy. Add the IIS role to your Windows Server setup in the Feature Manager and browse to the PHP IIS website. Once there, hit the big blue “Install PHP” button and let the Microsoft Web Platform Installer do it’s magic.

When you’re done, point your browser to this page. Another “install” button is presented. But for some reason this one does not always work (it didn’t in the Windows 2008 R2 Web edition I was using). So here’s how to get WinCache running Manually;
1. Download WinCache for PHP 5.3 from sourceforce.net.
2. Extract php_wincache.dll to “C:\Program Files (x86)\PHP\v5.3\ext”
3. Edit “C:\Program Files (x86)\PHP\v5.3\php.ini” and at the bottom of the file add the line “extension=php_wincache.dll”.
4. Extract wincache.php to your webroot. By default it’s somewhere like “C:\inetpub\wwwroot”.
5. Edit wincache.php and change the username and password in lines 42 and 43.
6. To be sure, restart IIS using yout IIS Manager.

Now browse to yourdefaultwebsite/wincache.php. If you hit a screen like the one below, your glasses are OK. If not, scream out lout and start asking for help…

WinCache statistics

WinCache statistics

Set up Magento to use WinCache
Back to Magento. The first part of WinCache, the opcode cache, kicks in a few levels below Magento. Magento does not know it’s there and does no need to. The opcode cache will keep a copy of the interpreted PHP opcode (created by the PHP binary when intepreting PHP files) in memory. It will use this copy each time a cached script is executed saving CPU cycles and IO operations (at the cost of a little memory). An opcode cache is vital for decent Magento performance. If your webhost does not implement one, sue them for stupidity (just kidding, don’t reference me in court).

The second part of WinCache we will want to use is the user cache. A user cache can be seen as a kind of dictionary. You can put data in it by attaching a label to it. When you want to extract that data at a later time you can access it by using that label. Magento comes with it’s own caching system. It can cache lots of things (System -> Cache management). It does this by utilizing the cache classes in the Zend Framework on which it is build.

To store all this cache it needs a backend. By default Magento will use the filesystem as its backend. It’s wise to keep it this way as the filesystem is the most likely to have space and is allways available. But it’s also wise to add a second backend that is a hell of lot faster then your filesystem. This is where we will want to use the WinCache data cache. Using these two backends is made possible by the TwoLevel cache. More on that in this great post by Fabrizio Branca.

Enable WinCache as the fast cache backend
The Zend Framework handles the backend itself, Magento just uses the interfaces/classes provided by the Zend Framework. At present the code required to use WinCache as a cache backend in not present in the Zend framework that ships with Magento. It is available in Zend framework 1.11. I’ve extracted the proper files here (download). Just download the zip and extract it’s contents to the root of your Magento installation to add WinCache data cache support to Magento 1.6+.

When that’s done we’ll need to configure Magento to use WinCache as it’s fast cache backend. Edit your app/etc/local.xml file and add these lines in the section;


     Zend_Cache_Backend_WinCache
     file

After saving your local.xml file be sure to flush Magento’s cache; System -> Cache management -> Select all -> Refresh -> Submit. When you check your wincache.php file again entries should show up under both “User cache” and “Opcode cache”.

URL Rewrites
Something else to think about when using Magento with IIS (or any other webserver then Apache) is to handle URL rewrites. IIS 7.5, which ships with Windows Server 2008R2, comes equipped with it’s own Rewrite module. By default the Magento installation contains .htaccess files which, among other things, instruct Apache’s URL Rewrite module how to handle the URL mappings. IIS does not use these .htaccess files. The IIS equivalent is the web.config file. Use a web.config file (which you have to save in your webroot) like the one below in order to use URL Rewrites (which you really should when doing this in production);






      
      
        
        
        
      
      
    
    



At this point we’re all done. Magento is up and running with IIS using WinCache.

This series of posts will compare typical, non-tweaked, LAMP and WIMP stacks, in terms of performance, running Magento CE 1.7.
Table of contents (work in progress)
1. Introduction
2. Running Magento with IIS and WinCache
3. Benchmarks (coming soon)
4. Conclusion (coming soon)

Magento LAMP vs WIMP: Introduction

When choosing a webserver stack, used to run PHP applications like Magento, a LAMP (Linux Apache MySQL PHP) stack is favored. Although nginx with php-fpm is on the rise, Apache on Linux is still the most popular choice. When choosing a reactor pattern based webserver like nginx or lightspeed, which some claim perform better under load, Linux is still the most popular choise. In fact here at RapidCommerce, all Magento hosting is done using Linux and a mix of Varnish/Apache/Nginx.

There is a “new” kid in town though, IIS. Obviously IIS does not run under Linux, its THE Microsoft Windows webserver. And it has never been popular to run PHP/MySQL based applications. With good reasons, I might add. IIS6 was a terrible performer when it came to PHP. However for IIS7 things have changed, so they say. Apparently the team behind IIS has been working closely with the PHP guru’s at Zend.

Add to these improvements in IIS7, the availability of a new opcode and user cache; WinCache. I started to wonder… How does this “new” WIMP (Windows IIS MySQL PHP) stack compare to the widely favored LAMP stack? So I decided to put things to the test. This series of posts will compare ZendServer CE (from those same gurus involved in improving IIS/PHP) with IIS7.

Both stacks can be seen as “out-of-the-box” PHP application servers. Both come with their own opcode cacher, Zend Optimizer+ with Zend Datacache SHM versus WinCache. Zend chose to run PHP as mod_php in Apache and in IIS PHP is run with FastCGI. I will acknowledge that adding Nginx/php-fpm and Apache/fast-cgi would be more fair, as they use a more comparable way to integrate PHP with the webserver. But I trust the guys at Zend know what they are doing and had their reasons to use the mod_php. The point being, out-of-the-box vs out-of-the-box. To make thing more comparable we’ll add Zend Server CE under Windows to the mix.

LAMP vs WIMP

This series of posts will compare typical, non-tweaked, LAMP and WIMP stacks, in terms of performance, running Magento CE 1.7.
Table of contents (work in progress)
1. Introduction
2. Running Magento with IIS and WinCache
3. Benchmarks (coming soon)
4. Conclusion (coming soon)

Configuration files with sensitive information are accessible from the outside.

So you’re migrating your Magento installation to another domain and you’re not wanting to wait for your domain name to transfer or for DNS to sync you get this warning in the Magento admin panel: “As a result, configuration files with sensitive information are accessible from the outside.”. That, when you do some research, seems to be associated with the /app/etc/local.xml being accessible from the outside.

But being the good system administrator you are, you made sure that either AllowOverride All was enabled on you Magento base directory or for the better administrators, you moved the config for each directory to Apache and set AllowOverride None, and you’re still getting this message?!

The problem resides in the code Magento uses to check if your config file with sensitive database password is accessible from the outside. It does this in Mage_Adminhtml_Block_Notification_Security.php;

    private function _isFileAccessible()
    {
        $defaultUnsecureBaseURL = (string) Mage::getConfig()->getNode('default/' . Mage_Core_Model_Store::XML_PATH_UNSECURE_BASE_URL);

        $http = new Varien_Http_Adapter_Curl();
        $http->setConfig(array('timeout' => $this->_verificationTimeOut));
        $http->write(Zend_Http_Client::POST, $defaultUnsecureBaseURL . $this->_filePath);
        $responseBody = $http->read();
        $responseCode = Zend_Http_Response::extractCode($responseBody);
        $http->close();

        return $responseCode == 200;
    }

This needs to be able to resolve itself. If the address still resolves to another address that will return an 200 OK page (perhaps your old domain) then the error will be displayed. This will fix itself when DNS syncs. Or alternatively you can make sure your new domain name is added to /etc/hosts file or c:\Windows\System32\Drivers\etc\hosts.

Magento back to overview button in product detail view

The breadcrumbs block provide the user with the ability to seamlessly navigate your Magento webshop. Sometimes however it’s nice to have a big, fat, BACK TO OVERVIEW button for visitors that have problems reading or who see no further than (the end of) one’s nose. In Magento it’s possible to navigate to a product in several ways (search, catalog, CMS etc etc) and a product can exists in several categories.

So for the back button I would rely on the breadcrumbs, as they present us with all the functionality we need for our back to overview button. So adding the button to our breadcrumbs block template is the way to go (it actually came in handy considering the design in this case, but you can use the layout XML to add more breadcrum children with different templates if this is not the case).

Edit app/design/frontend/YOURTEMPLATE/default/template/page/html/breadcrumbs.phtml and change it to something like this:








	

This will provide you with a nice button. One that only shows up in the product detail view. Or remove the Mage::registry(‘current_product’) check to make it work everywhere.

Magento: Set number of columns in grid

A quick Magento tip.

If you would like to set the number of columns in a grid, you can do this using the layout.xml. Just call setColumnCount using <action />. This has to be done on the grid block.

For example, if you would like to set the number of columns in the product grid on the category page, you could do this using the following XML.

<catalog_category_default>
    <reference name="product_list">
        <action method="setColumnCount"><count>3</count></action>
    </reference>
</catalog_category_default>

The name of the parameter (<count/>) could be anything, but must be specified. Just specifying 3 between <action/> doesn’t work.

Magento product list by SKU

So lets say you wrote an article somewhere in your Magento webshop. Using a CMS page or static block you added your content and now you want to add a selection of products to be displayed with your content.

Magento offers you the possibility, out-of-the-box, to add articles and a category and then display that list in a CMS contet block by inserting:

{{block type="catalog/product_list" name="home.catalog.product.list" alias="products_homepage" category_id="4" template="catalog/product/list.phtml"}}

This however requires you to create a separate hidden category. So sometimes it would be easier if you could tell the list block exactly which products you want displayed. To do this we’ll create a small extension and overwrite the core Mage_Catalog_Block_Product_List block.

First step is to create a directory for our new extension, create these folders;

  • /app/code/local/MyCompany
  • /app/code/local/MyCompany/Bysku
  • /app/code/local/MyCompany/Bysku/etc
  • /app/code/local/MyCompany/Bysku/Block

Now that we have our directory structure it’s time to create a block that will extend the one that came with Magento.

Create the file /app/code/local/MyCompany/Bysku/Block/List.php and put this code in the file:

getSkus()) // Check is the user gave us the skus parameter containing products
 {
 $arrSkus = explode(',', $this->getSkus()); // Explode the comma seperated sku list to an array
 $collection = Mage::getModel('catalog/product')->getCollection(); // Start a new collection containing products
 $collection->addAttributeToSelect('*');     // Tell magento to load all the product attribute data, change this if you need just a subset of data in your template file
 $i = 0; $filters = array(); // Init some vars to add our filters
 foreach($arrSkus as $sku)
 $filters[$i++] = array('attribute'=>'sku','eq'=>$sku); // For each SKU add a filter that defines that the product will be selected is it's sku is in our array
 $collection->addFieldToFilter($filters); // Add the filter
 $this->setCollection($collection); // Set this collection to be the one we're using
 return $collection; // Return it
 } else {
 return parent::_getProductCollection(); // No skus parameter, just behaive like our base class would
 }
 }
}
?>

Now that we have defined our logic, it’s time to tell Magento to load our wonderful list extension. To do this, we need to create the file /app/code/local/MyCompany/Bysku/etc/config.xml and put this XML in the file:



 
 
 1.0.0
 
 
 
 
 
 MyCompany_Bysku_Block
 
 
 

After this, just reload your cache in the admin panel, System -> Cache managent -> Selected all -> Refresh. Then edit your CMS block and add this code:

{{block type="bysku/list" name="home.catalog.product.list" alias="products_homepage" skus="mysku1,mysku2,mysku3" template="catalog/product/list.phtml"}}

Now your content will have a nice list of dynamically loaded products by SKU… Enjoy!

Magento, TinyMCE and empty divs

Some JS scripts require empty divs, or perhaps you like to use them in combination with CSS to build blocks, menu’s etc.
By default TinyMCE does not allow you to use empty div’s and it will remove them without further notice. So even if you click “Show/hide editor” in Magento 1.4+.x and add your div it will disappear…

You can overcome this nasty behaviour by adding an option to the settings array Magento presents to the TincyMCE javascript.

In /js/mage/adminhtml/wysiwyg/tiny_mce edit the file setup.js and add the following line:
extended_valid_elements : “div[*]”,
Somewhere between var settings = { and };
(except you the last line, ad you should remove the last comma for syntax reasons… (duh))

(I would recommend cleaning your browsers cache after this) Reload the page, et voilá! You can now add empty div’s like “

“.

Magento 1.5.0.1 include_in_menu trough API

When working on a Magento 1.5.0.1 I noticed that the required attribute “include_in_menu” is not present in the WSDL. If you autogenerate a proxy using this WSDL your code will break at catalogCategoryCreate() with a Soap Fault stating “Attribute include_in_menu is required.”

The solution is easy, upgrade to 1.5.1.0 as they apparently fixed this bug.

Magento 1.4.x Sales email totals sort issue

A customer running a 1.4.x store asked me to change the order of the totals, more specifically to display the tax line last. We build some modules that had their own custom totals (packaging, storage costs etc). All of those add up to the total. But since the grand total is displayed including Tax (as is custom in the Netherlands) the “VAT” line displayed above the total line was confusing to customers, as all lines are already displayed including tax.

Magento supports changing the sort order build in (System -> Configuration -> Sales ->Checkout Totals Sort Order) but somehow this does not seem to apply to the sales emails. The tax block gets added in sales.xml in your layout folder. And it’s output is generated in /app/design/frontend//default/template/sales/order/totals.phtml. In this file you’ll find a foreach loop which ignores the sort order.

So not wanting to modify the core or charging the customer for writing a custom module to do this, the simple solution was to modify the template.
This was to simply filter out the tax total in that foreach loop, ignoring it and and the end of the file echo it’s output. You can modify your file like so (off course changing tax to the code of other total lines will also work);


getTotals() as $_code => $_total): ?>
	
		getBlockName()): ?>
			getChildHtml($_total->getBlockName(), false); ?>
		
		
			getLabelProperties()?>>
				getStrong()):?>
				getLabel()?>
				
				getLabel()?>
				
			
			getValueProperties()?>>
				getStrong()):?>
				formatValue($_total) ?>
				
				formatValue($_total) ?>
				
			
		
		
	
		 
	



	getChildHtml($_taxTotal->getBlockName(), false); ?>