Edwin V. http://edwinv.nl Most recent posts at Edwin V. posterous.com Mon, 28 Aug 2006 13:34:14 -0700 Financiële steun voor Zend http://edwinv.nl/php/financiele-steun-voor-zend http://edwinv.nl/php/financiele-steun-voor-zend

Tweakers.net bericht dat verschillende investeerders Zend een financiële injectie hebben gegeven. Zend gaat het geld gebruiken om zijn activiteiten in Europa en Azië uit te breiden en de programmastructuur uit te breiden met ondersteuning voor Ajax.

Originele artikel

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/586042/9f86e6f9bff86461d58ab78f8988051d.png http://posterous.com/users/5AfGsE869oYx Edwin Vlieg winno Edwin Vlieg
Tue, 22 Aug 2006 14:01:48 -0700 PHP ter ondersteuning van de opmaak http://edwinv.nl/php/php-ter-ondersteuning-van-de-opmaak http://edwinv.nl/php/php-ter-ondersteuning-van-de-opmaak

Vaak zal een server-side taal als PHP niet snel gekoppeld worden aan de opmaak van een website. Toch kan je op het gebied van weergave hele interessante dingen doen met PHP, zo laat A List Apart zien.

In uitgave 222 van het online magazine staat een artikel over Sandbags ter ondersteuning van de uitlijning van tekst rond afbeeldingen. Door onzichtbare elementen over een afbeelding te plaatsen is het mogelijk om de tekst soepel om een afbeelding met transparante achtergrond te draperen.

Het gedeelte waar PHP in beeld komt, is het uitlezen van de transparante pixels in de afbeelding. Door elke rij in de afbeelding langs te gaan en te bepalen waar de werkelijke afbeelding begint, is het mogelijk om div’s heel nauwkeurig te positioneren en zo de tekst uit te laten lijnen. Dit idee bleek niet altijd te werken, maar uiteraard hebben ze daar een prima oplossing voor gevonden.

Kortom: een interessant artikel over de perfecte samenwerking tussen server- en client-side talen om het leven van de webdeveloper wat makkelijker te maken. En de tijd die daardoor beschikbaar komt kan je mooi gebruiken om….

Save time by tricking PHP into managing your tricky text-wrap problems; use that time to fix your About page. Everybody wins.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/586042/9f86e6f9bff86461d58ab78f8988051d.png http://posterous.com/users/5AfGsE869oYx Edwin Vlieg winno Edwin Vlieg
Sun, 20 Aug 2006 18:13:14 -0700 De perfecte opbouw van een webapplicatie http://edwinv.nl/webdevelopment/de-perfecte-opbouw-van-een-webapplicatie http://edwinv.nl/webdevelopment/de-perfecte-opbouw-van-een-webapplicatie

Begin dit jaar schreef Tri Pham een interessant artikel op Scriptorama over de perfecte webdeveloper. Zijn conclusie was simpel maar doeltreffend:

De perfecte web developer bestaat misschien niet, maar er naar streven doet geen vlieg kwaad.

Een manier om hier naar te streven is voor mij een goede opbouw van een applicatie. De laatste tijd heb ik verschillende verzoeken ontvangen om te adviseren bij het ontwikkelen van een webapplicatie. Omdat het hier niet om kleine websites gaat, is de opbouw van groot belang. In dit artikel ga ik proberen de perfecte web developer uit te hangen en mijn mening geven over de perfecte opbouw van een webapplicatie.

A key thing about patterns is that you can never just apply the solution blindly, which is why pattern tools have been such miserable failures. I like to say that patterns are “half baked,” meaning that you always have to finish them off in the oven of your own project. Every time I use a pattern I tweak it a little here and a little there. You see the same solution many times over, but it’s never exactly the same.

De bovenstaande uitspraak komt uit het boek Patterns of Enterprise Application Architecture van Martin Fowler. In dit artikel zullen veel design patterns aan bod komen, patterns die ik zelf handig vind om te gebruiken. Luister naar Fowler, blijf altijd nadenken over de opbouw van je eigen applicatie!

De opbouw van webapplicaties is meestal in grote lijnen met elkaar te vergelijken. Je hebt te maken met de weergave van de applicatie in een webbrowser. Daarnaast zal er een bepaalde gegevensbron zijn, bijvoorbeeld in de vorm van een database. Het laatste element is de koppeling tussen de weergave en de database. Dit element zal de aanvraag van een client omzetten naar een weergave van gegevens uit de database.

De drie bovenstaande elementen zijn beter bekend als Model, View en Controller. Het MVC pattern is in mijn ogen daarom een goede basis voor een webapplicatie.

Model

Het model van een webapplicatie bestaat uit de koppeling tussen de applicatie en een gegevensbron. In veel gevallen hebben hier te maken met een database. De gegevens in een database bestaan uit verschillende entiteiten die inhoud kunnen bevatten. In verschillende projecten in het verleden heb ik ervoor gekozen om voor elke tabel een klasse aan te maken die er globaal als volgt uit ziet:

<?php
class PageModel extends Model {

public function getPages(){
return $this->query("SELECT * FROM pages");
}

public function getPage($iId){
return $this->query("SELECT * FROM pages WHERE id = ".$iId);
}

public function addPage($sName, $sLocation, $bPublished){
return $this->query("INSERT INTO ...");
}

public function editPage($iId, $sName, $sLocation, $bPublished){
return $this->query("UPDATE pages SET ...");
}

public function deletePage($iId){
return $this->query("DELETE FROM ...");
}

}
?>

De methoden werden meestal aangevuld met controles op de gegeven parameters. De Model klasse bevatte alle functionaliteit voor de koppeling met de database. Vaak gaf de methode ‘query’ al associatieve arrays terug met gegevens zodat het in de applicatie verder niet nodig was om te werken met mysql_* functies. Het voordeel hiervan was ook de uitwisselbaarheid met andere database typen, zoals PostgreSQL.

De bovenstaande opzet van een model klasse werkt, alleen doe je regelmatig dubbel werk. De queries in de verschillende klassen zullen voor het grootste gedeelte gelijk zijn. Daarnaast mis je veel flexibiliteit. Zodra je in een controller of view een andere sortering van de gegevens wilt dien je gelijk aanpassingen te maken in de model klasse. Er is dus nog veel ruimte voor automatisering van een model klasse.

Dat laatste is iets waar ik veel belang aan ben gaan hechten. Dubbel werk moet niet nodig zijn als je een goede opbouw van je applicatie hebt, DRY dus. Helemaal als je gebruik gaat maken van object-georiënteerd programmeren. Versie 5 van PHP biedt de hiervoor voldoende mogelijkheden, al ben ik zelf meer een Ruby aanhanger aan het worden. Ruby is zelf een native OOP taal waardoor er veel meer mogelijk is op dat gebied.

Een goed design pattern voor het model is in mijn ogen ActiveRecord. Binnen verschillende frameworks wordt dit pattern toegepast en mijn ervaring is dat het geweldig werkt. Het biedt alle mogelijkheden om DRY te werken, maar je kunt altijd nog zelf je queries gaan schrijven als het nodig is.

Een simpel voorbeeld:
<?php
class Person extends ActiveRecord {

}

// SELECT * FROM persons WHERE id = 1
$person = Person::find(1);
echo $person->name;

// UPDATE persons SET name = 'Edwin V.' WHERE id = 1
$person->name = "Edwin V.";
$person->save;
?>

Zoals je ziet is het overbodig om queries te schrijven, ActiveRecord stelt zelf de queries samen. Het hangt van de implementatie van ActiveRecord af wat allemaal mogelijk is. De implementatie binnen Ruby on Rails biedt bijvoorbeeld functionaliteit voor associaties en gegevens validatie. Het Zend Framework biedt helaas geen implementatie van ActiveRecord aan, maar wel andere handige database klassen.

Een interessant overzicht van object-relational mapping implementaties in PHP is te vinden op de PHP wiki.

View

Een net zo belangrijk onderdeel van een applicatie is de weergave richting de gebruiker. Bij een webapplicatie gebeurd dat meestal in een browser, dus de weergave bestaat uit HTML en de bijbehorende afbeeldingen, JavaScripts en stylesheets. Daarnaast is het natuurlijk mogelijk om XML als weergave te hebben, voor bijvoorbeeld gebruik in een andere applicatie of als RSS feed.
Een laatste – redelijk nieuwe – techniek is JavaScript. In dat geval zal als reactie op een Ajax request vanuit de browser een stukje JavaScript terug gezonden worden om uitgevoerd te worden in de browser. Deze manier van werken wordt de laatste tijd meer toegepast door de introductie van RJS templates in Rails. Een korte introductie van RJS is te vinden op de weblog van Cody Fauser.

De belangrijkste taak van de view is dus de gegevens uit de controller omzetten naar een passend formaat. Hiervoor kan je vaak gebruik maken van een template engine zoals Smarty of Yapter. Mijn ervaring is dat template engines meestal overbodig zijn omdat PHP zelf al een perfecte template engine is.

<ul>
[LOOP $items]
<li>[$item]</li>
[ENDLOOP]
</ul>

<ul>
<? foreach($this->items as $item): ?>
<li><?= $item ?></li>
<? end; ?>
</ul>

Het bovenste voorbeeld zou uit een template engine kunnen komen. Het doet precies wat je wilt, maar absoluut niet meer. Het onderste voorbeeld is dezelfde code in PHP. Als nadeel kan je het aantal tekens noemen, maar dat weegt zeker op tegen de voordelen. Je kunt namelijk gewoon alle functionaliteit gebruiken die PHP te bieden heeft.

Een goede implementatie van een View is die van het Zend Framework. Hierbij ken je in de controller waarden toe aan variabelen in het view object. Deze variabelen kan je vervolgens weer gebruiken in je template.

Binnen templates kan het handig zijn om met helpers te werken. Dit principe kennen we onder andere van Rails en zit wederom in het Zend Framework. Helpers bevatten functies die veel gebruikte HTML tags genereren. Ze kunnen bijvoorbeeld helpen bij het samenstellen van een formulier voor een bepaald object.

Controller

De taak van de controller klassen is een request van een gebruiker omzetten naar een bepaalde weergave van de staat van het systeem. Een request van een gebruiker bestaat meestal uit een URL met daarin bepaalde informatie over de op te vragen gegevens. Een veel gebruikte opzet bestaat uit:
* Controller: De entiteit die opgevraagd wordt, bijvoorbeeld ‘person’
* Actie: De actie die uitgevoerd wordt op de entiteit, bijvoorbeeld ‘show’
* ID: De unieke identifier naar de entiteit, bijvoorbeeld ’1′.

Een aanvraag kan er dus uit zien als ‘index.php?controller=person&action=show&id=1′, maar vaak wordt gebruik gemaakt van nette URL’s en ziet de URL er zo uit: ‘/person/show/1′. Voor elke entiteit bestaat vervolgens een controller:

<?php
class PersonController extends ApplicationController {

public function showAction(){
// Show Person with given ID
}
}
?>

Een controller bevat voor elke mogelijke actie een methode. In het verleden heb ik wel gewerkt met speciale klassen voor elke actie, maar mijn ervaring is dat dit erg onoverzichtelijk is. Het is aan te raden om voor elke actie een methode te gebruiken.

Het omzetten van de URL naar een aanroep van een methode in een controller gaat meestal via routing. De implementatie van controllers in het Zend Framework bevat een RewriteRouter waarmee dit makkelijk te realiseren is. Zodra het routing gedaan is kan de methode de benodigde acties uitvoeren. Een voorbeeld van een controller met twee acties is:
<?php
class PersonController extends ApplicationController {

public function showAction(){
$view = new View();
$view->person = Person::find($this->params['id']);
return $view->parse('person/show.tpl');
}

public function editAction(){
if($this->request->isPostMethod()){
$person = Person::find($this->params['id']);
$person->name = $this->params['person']['name'];
$person->save();
return $this->redirect('person', 'show', $person->id);
} else {
$view = new View();
$view->person = Person::find($this->params['id']);
return $view->parse('person/edit.tpl');
}
}

}
?>

Conclusie

Zoals uit het artikel blijkt ben ik zelf een grote fan van Ruby on Rails en het Zend Framework. Het Rails framework geeft webdevelopers een kant-en-klare oplossing voor de opbouw van een webapplicatie. Zowel het MVC pattern als ActiveRecords is hierin terug te vinden, beide met een zeer goede implementatie.

Het Zend Framework is helaas nog in een beta status en dwingt niet de opbouw van een applicatie af. Ze geven echter wel een goede basis met verschillende klassen ter ondersteuning van MVC en object-relation mapping. Hoewel ik de laatste tijd veel werk met Ruby, zou ik het framework zeker gebruiken als basis voor een PHP applicatie.

De genoemde opbouw van een applicatie is in mijn ogen een manier om DRY te werken en toch voldoende flexibiliteit te hebben. De methode heeft al meerdere keren goede dienst bewezen bij mij. En om net zo’n pakkende conclusie als Tri Pham te geven:

Een perfecte opbouw van een webapplicatie bestaat niet, maar er naar streven doet geen vlieg kwaad.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/586042/9f86e6f9bff86461d58ab78f8988051d.png http://posterous.com/users/5AfGsE869oYx Edwin Vlieg winno Edwin Vlieg
Sat, 03 Jun 2006 06:57:24 -0700 Zend Studio 5.2 http://edwinv.nl/php/zend-studio-52 http://edwinv.nl/php/zend-studio-52

Afgelopen week heeft Zend een nieuwe versie van de veel gebruikte Zend Studio uitgegeven. Er zijn enkele wijzigingen gemaakt aan de stabiliteit van het programma en het is nu mogelijk om nog sneller je applicaties te maken.

Een gratis trail kan hier gevonden worden, meer informatie over dit product is te vinden op de product pagina van Zend.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/586042/9f86e6f9bff86461d58ab78f8988051d.png http://posterous.com/users/5AfGsE869oYx Edwin Vlieg winno Edwin Vlieg
Tue, 18 Apr 2006 20:31:07 -0700 Zend Framework: preview versie 0.1.3 http://edwinv.nl/webdevelopment/zend-framework-preview-versie-013 http://edwinv.nl/webdevelopment/zend-framework-preview-versie-013

Zend heeft net een nieuwe preview van hun framework openbaar gemaakt. Na een herstyle van de framework website afgelopen week, is er nu dus ook weer wat aan de code gedaan. Overigens is het ook mogelijk om rechtstreeks in de SVN repository een checkout te doen, zodat je de allerlaatste preview hebt.

De changelog van de nieuwe versie ziet er als volgt uit:

RELEASE 0.1.3 / 18-Apr-2006

  • Zend_Filter is* methods return strictly TRUE or FALSE. (Chris)
  • Zend_InputFilter has test* methods for retrieving valid data. (Chris)
  • Fixed bug in Zend_View_Abstract::__isset(). Reported by James Simmons. (Mike)
  • Zend_Db_Adapter_Pdo_Mysql::limit() now compatible with MySQL versions prior to 4.0. Reported by Greg Neustaetter (Mike)
  • Fixed bug in Zend_Controller_Dispatcher_Token::setParams(). Reported by Rob Allen. (Chris)
  • Fixed bug in Zend_Log::log(). Reported by Mislav. (Mike)
  • Updated Zend_Filter::isFloat() and Zend_Filter::isInt() to respect locale. (Chris)
  • Improved Zend_Db_Adapter_Pdo_Mssql contributed by Rob Allen. (Mike)
  • Fixed bug in Zend_Controller_Dispatcher::_formatName. Reported by Arpad Ray. (Chris)
  • Zend::dump() now works from CLI (Rob Allen)
  • Improved support for XML-RPC namespaces (Mike, Chuck)
  • Registry can now be tested with Zend::isRegistered (Shekar C. Reddy, Mike)
  • Zend_Search_Lucene promoted from incubator (Alex)
  • Zend_Cache has been accepted to the incubator (Fabien, Mislav)
  • Zend_Json testing expanded; covers all major cases (Matthew)
  • Fixed Zend_Json encoding of empty values (Matthew, Davey)
  • Fixed Zend_Json encoding of associative arrays (Matthew, Davey)
  • Fixed Zend_Json encoding of numeric indices in associative arrays (Matthew)
  • Removed formatting (newlines, tabs) from Zend_Json encoding methods (Matthew)
  • Fixed escaping in Zend_Json_Encoder (Matthew)
  • Zend_HttpClient moved to Zend_Http_Client (Mike)
  • Zend_Console_Args in the incubator but not yet refactored (Jason Garber)
  • Zend_Mail enhancements in the incubator by Austria Telekom (Nico, Clez)
  • Zend_Service classes no longer subclass Zend_Service_Rest (Davey, Andi, et al)
  • Zend_Service classes now use new Zend_InputFilter (Davey)
  • Fixed bug in Zend_Service_Amazon::itemLookup() (Davey)
  • Fixed bug in Zend_Service_Flickr::userSearch() (Davey)
  • Fixed bug in Zend_Uri_Http::__construct(). Reported by Adrian Gheorghe. (Mike)
  • Improved some not well-formed PDF processing with Zend_Pdf. (Alexander)
  • Minor Zend_Pdf documentation fixes. (Alexander)
  • Fixed Zend_Pdf processing of inherited page attributes. (Alexander)
  • Fixed Zend_Pdf umlauts support for standard fonts. (Alexander)

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/586042/9f86e6f9bff86461d58ab78f8988051d.png http://posterous.com/users/5AfGsE869oYx Edwin Vlieg winno Edwin Vlieg
Sat, 11 Mar 2006 17:05:36 -0800 Zend Framework: Scaffolding http://edwinv.nl/webdevelopment/zend-framework-scaffolding http://edwinv.nl/webdevelopment/zend-framework-scaffolding

The current version of Zend Framework doesn’t include a way to use scaffolding in a script-in-development. Because I wanted to play with the framework, I’ve extended the Zend_Controller_Action class to make scaffolding possible. In this article I shall explain the code and give some feedback for the developers of the framework.

The main purpose for scaffolding is creating very basic pages for viewing and editing content in the database. In an early stage of implementation, scaffolding gives the developer a way to test certain functionality, without the need to work directly in the database. Besides that, scaffolding is used as a basis for the final implementation.

In this example, I’m using the Action and Router class from Akra’s DevNotes to create a simple application. In fact, de Scaffold class will extend the Akrabat_Action class, which hopefully in the future will be part of the framework itself.


h3. Creating the model

First of all, we need to create a new data object to apply our Scaffold class to. Create a new table in your favorite database and create a new model class for the database:

<?php
class tableName extends Zend_Db_Table { }
?>

‘tableName’ matches the name of the table you just created, in this case ‘table_name’. Make sure the column name which contains the primary id of the table is ‘id’ or define another primary column.

Creating the controller class

To manage the data inside the table, we want to create a controller class like this:

class tableNameController extends Fly_Scaffolding { }

The controller ‘tableName’ should give us the possibility to:
* Show a list of rows in the table
* Show a single row in the table
* Edit a row
* Delete a row
* Add a row

The Scaffolding class

The code for the Fly_Scaffolding class is:

<?php

/**
* This file contains the scaffolding class
*
*
* @copyright 2006 Edwin V.
* @license http://www.zend.com/license/3_0.txt PHP License 3.0
* @version CVS: $Id:$
* @link
*/

require_once 'Zend/Db/Inflector.php';

/**
* Scaffolding class, extends the Akrabat_Action class
* Provides the developer a simple CRUD interface for a table
*
* @copyright 2006 Edwin V.
* @license http://www.zend.com/license/3_0.txt PHP License 3.0
* version Release: @package_version
* @link
*/
class Fly_Scaffolding extends Akrabat_Controller_Action {

/**
* For name inflections.
*
* @var Zend_Db_Inflector
*/
static protected $_inflector;

/**
* The name of this controller
*
* @var string
*/
protected $_name;

/**
* The name of this entity, this is the table name without the given prefix
*
* @var string
*/
protected $_entityName;

/**
* The table to scaffold
*
* @var Zend_Db_Table
*/
private $_table;

/**
* The information about the table
*
* @var array
*/
private $_tableInfo;


/**
* Construct a new scaffolding object
*/
public function __construct($tableName = ""){
parent::__construct();

// Use the db inflector
self::$_inflector = new Zend_Db_Inflector();

if(empty($tableName)){
$this->_name = str_replace("Controller", "", get_class($this));
} else {
$this->_name = self::$_inflector->camelize($tableName);
}

// Replace fly with some kind of table prefix
$this->_entityName = $this->_name;

require_once("../application/models/".ucfirst($this->_name).".php");

$this->_table = new $this->_name();

$this->_tableInfo = $this->_table->info();

// Define the singular and plural name for use in the views
$this->nameSingular = self::$_inflector->singular($this->_entityName);
$this->namePlural = self::$_inflector->plural($this->_entityName);

$info = $this->_table->info();
$this->primary = strtolower($this->_tableInfo['primary']);

}

public function indexAction(){
$this->_redirect('list');
}

/**
* The list action shows a list of all rows in the table
*/
public function listAction(){
$info = $this->_table->info();
$this->columns = $info['cols'];
$this->items = $this->_table->fetchAll();
}

/**
* The show action shows a single row in the table
*/
public function showAction(){
$this->item = $this->_table->find($this->_getParam('id'));

// Create array because of bug
$data = $this->item->toArray();
$this->id = $data[$this->primary];
}

/**
* The edit action shows a form for editing a row in the table
*/
public function editAction(){
$this->item = $this->_table->find($this->_getParam('id'));
}

/**
* The update action updates the table with information from the edit form
*/
public function updateAction(){
// Add some kind of post method check
// check if post variables are available

$id = $_POST[$this->primary];

// We don't want to update the primary column
unset($_POST[$this->primary], $_POST['submit']);

$where = $this->_table->getAdapter()->quoteInto($this->primary." = ?", $id);
$this->_table->update($_POST, $where);

$this->_redirect('show/id/'.$id);
}

/**
* The add action displays a form for adding a new row to the table
*/
public function newAction(){
$this->columns = $this->_tableInfo['cols'];
}

/**
* The create action adds a new row to the table
*/
public function createAction(){
unset($_POST['submit']);

$id = $this->_table->insert($_POST);
$this->_redirect('show/id/'.$id);
}

/**
* The delete action deletes a row from the table
*/
public function destroyAction(){
$where = $this->_table->getAdapter()->quoteInto($this->primary." = ?", $this->_getParam('id'));
$this->_table->delete($where);
$this->_redirect('../../list');
}


}

This class extends the action class and defines eight new actions for the controller. For the list, show, edit and new action we create views to display the information delivered by this controller. The view files can be found in the zip file, together with an application to test the scaffolding class.

Suggestions for improvement of the Scaffold class

The given implementation of the Scaffold class is just an example of how it can be implemented. There are a lot of improvement points and possible security problems.

Some suggestions for improvement:

  • Move the view files to the Zend Framework path, so users don’t have to copy it to their application path.
  • Check if a view is available, if not, use the framework default view for the action (but therefor the above point should be solved first)
  • Display form elements according to the column type
  • Check if the request method of the update and create actions is post

Feedback

Things I’m missing in the current version of the framework and bugs I’ve found:

  • There is no possibility to get the current url of the page. If you want to create a link to a different action in the same controller, you should always read the current url and do some tricky actions. Maybe a function like this can be interesting:
    // Returns the value of the controller part and the action part of the url,
    // in case of requesting 'tableNameController/edit/id/1' this would return 'tableNameController/edit'
    $this->getUrl("controller/action");
  • In the form helper functions, it is possible to define html tag arguments in the array. In case of the ‘disabled’ argument of a form element, giving a boolean always puts the disabled argument in the tag. Actually, you only want to add the disabled argument when the argument is true. In case of false, the output would be ‘disabled=”"‘, which still means that the element is disabled.
  • A new function in the inflector class for creating human readable column names: ‘site_id’ should become ‘Site id’. Maybe make the inflector class available as a helper in a view?
  • A way to detect if a request is post or get and if there are certain post variables available. For example in the update method in my class this can be useful. The update method should only be parsed succesful when the method is post and the post variables that existed in the edit form are available.
  • With the Zend_Db_Table::update method, it is possible to update some rows in the table. The method doesn’t check if the given keys are valid columns in the table.
  • “Notice: Undefined index: ID in e:public_htmlZFlibraryZendDbTableRow.php on line 95″ When using ID as a primary column, ZF sometimes makes the column lowercase. In this case, the column exists, but the value is stored in ‘id’, so this error is thrown.
  • Create a special Zend_Db_Row property which contains the value of the primary key:
    echo $this->item->_primary;
    // instead of
    $data = $this->item->toArray();
    $info = $this->_table->info();
    echo $data[$info['primary']];
  • Make it possible to have table prefixes that do not need to be in the classname of the class that extends Zend_Db_Table or Fly_Scaffolding
  • Improve the table classes to:
    • Define validations for table contents which can easily be read to use in add/edit forms.
    • Define relations between tables
    • Make is possible to read the column types of the table to use appropriate form elements

I really hope that there will be some kind of scaffolding in the stable version of the Zend Framework. With one small class and a few views, the work of many developers can be made a lot easier.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/586042/9f86e6f9bff86461d58ab78f8988051d.png http://posterous.com/users/5AfGsE869oYx Edwin Vlieg winno Edwin Vlieg
Wed, 08 Mar 2006 10:17:00 -0800 Zend Framework 0.1.2 http://edwinv.nl/webdevelopment/zend-framework-012 http://edwinv.nl/webdevelopment/zend-framework-012

Vandaag is een nieuwe preview van het Zend Framework uitgekomen.

Mike Naberezny:
“Also, we are hoping to roll out a new preview release today that will include additional documentation, the unit test suite, and fixes for all bugs that have been reported to date. We hope new preview releases will be a very frequent event. “

De bijbehorende changelog is:
* Fixed default charset in Zend_Mail constructor. Reported by Jakob Buchgraber. (Mike)
* Fixed several Zend_Filter methods. (Chris)
* Fixed JSON datum encoding. Reported by Edwin V. (Mike)
* Fixed FormRadio Helper. Reported by AJ Tarachanowicz. (Chris)
* Fixed Zend_Uri_Http to work with new Zend_Filter. (Chris, Mike)
* Docs for the Zend_Db::factory() method were incorrect. Reported by Dinh. (Chris)
* Zend::loadClass() now works inside __autoload(). Reported by Rob Allen. (Mike)
* Fixed notices from Zend_Pdf_Element_Dictionary. Reported by Ralf Eggert. (Alex)
* Fixed notices from Zend_Search_Lucene_Index_SegmentWriter. Reported by Jared Williams. (Alex)
* Removed defunct Zend_Db_DataObject docs. (Mike)
* Added NEWS.txt file (Andi)

De nieuwe versie is te downloaden vanaf hier.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/586042/9f86e6f9bff86461d58ab78f8988051d.png http://posterous.com/users/5AfGsE869oYx Edwin Vlieg winno Edwin Vlieg
Mon, 06 Mar 2006 18:35:06 -0800 Zend Framework http://edwinv.nl/webdevelopment/zend-framework http://edwinv.nl/webdevelopment/zend-framework

Na veel onduidelijkheid over het uitkomen van een nieuw framework, is de eerste preview eindelijk beschikbaar. Het Zend Framework is een product van het PHP Collaboration Project dat is opgestart door Zend. Met medewerking van onder andere IBM, Intel en MySQL wil Zend de taal PHP een nieuw leven in blazen.

De drie producten die het resultaat zijn van deze samenwerking zijn:
* Het Zend Framework
* De Zend Developer Zone
* Een Eclipse-based IDE (Op dit moment nog een proposal)

Het Het Zend Framework biedt een degelijke basis voor het werken met PHP. Gebaseerd op de nieuwe technieken in PHP 5 krijg je een volledig OOP framework met een database abstractie layer, MVC pattern en Lucene search. Alles is grondig gedocumenteerd, al staan er op dit moment nog zaken in de manual die nog niet in het framework zitten.
In het framework zelf zitten helaas nog veel bugs, maar we mogen natuurlijk niet te hoge eisen stellen van een preview versie. De ontwikkelaars gaan zeer netjes om met bug reports en we mogen binnenkort een nieuwe preview verwachten/

Enkele nuttige artikelen over het framework zijn hier te vinden:
* Zend Controller
* Zend Framework Front Controller
* Zend Framework Views and the Front Controller
* Zend Framework review-internals en werking

Op de De Zend Developer Zone kan je interessante artikelen vinden over PHP. Een reeks over patterns is er nu al te vinden en belooft veel goeds voor de toekomst.

De Eclipse-based IDE is op dit moment nog een proposal, maar zal in de toekomst een gratis alternatief voor Zend Studio gaan worden. Ook dit is weer een zeer nobel initiatief!

Conclusie: al staat dit project nog in de kinderschoenen, het belooft veel goeds voor de toekomst.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/586042/9f86e6f9bff86461d58ab78f8988051d.png http://posterous.com/users/5AfGsE869oYx Edwin Vlieg winno Edwin Vlieg
Thu, 16 Feb 2006 20:30:08 -0800 MySQL foutafhandeling in PHP http://edwinv.nl/php/mysql-foutafhandeling-in-php http://edwinv.nl/php/mysql-foutafhandeling-in-php

Regelmatig zie ik de vraag langs komen over foutafhandeling voor MySQL in PHP. Gebruikers krijgen dan een onduidelijke foutmelding van mysql_fetch_*, terwijl de fout eigenlijk heel ergens anders ligt.

<?php
$rResult = mysql_query("SELECT * FROM tabel WHERE iets=true");
while($aRow = mysql_fetch_assoc($rResult)){
echo $aRow['kolom'];
}
?>

Het is mogelijk dat in mysql_query een fout optreed, maar doordat MySQL niet standaard een foutmelding op het scherm weergeeft, kan het voorkomen dat je hier verder niets van merkt. Pas als je bij mysql_fetch_assoc terecht komt geeft PHP een foutmelding, omdat in $rResult een boolean ‘false’ zit en niet de verwachte resource.

Dit is op te lossen door de functie mysql_error te gebruiken. Hiermee kan je netjes de foutmelding op het scherm weergeven:

<?php
$sSql = "SELECT * FROM tabel WHERE kolom=iets";
$rResult = mysql_query($sSql);
if(!$rResult){
echo "Er ging iets fout: ".mysql_error()." (Query: ".$sSql.")";
} else {
while($aRow = mysql_fetch_assoc($rResult)){
echo $aRow['kolom'];
}
}
?>

Hierdoor zal je bij een mogelijke fout in je query een nette foutmelding krijgen.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/586042/9f86e6f9bff86461d58ab78f8988051d.png http://posterous.com/users/5AfGsE869oYx Edwin Vlieg winno Edwin Vlieg
Tue, 14 Jun 2005 09:26:41 -0700 Whois script http://edwinv.nl/webdevelopment/whois-script http://edwinv.nl/webdevelopment/whois-script

De onderstaande klasse voor PHP 5 kan gebruikt worden om een whois query uit te voeren. Hiermee kan je controleren of een domeinnaam beschikbaar is. Er wordt gebruik gemaakt van de ‘whois is’ methode bij .nl domeinen, zodat het limiet van 15 aanvragen per dag omzeilt wordt.

<?php

/**
* Whois class
* Copyright by Edwin Vlieg - Flydesign.nl
*
* Example use:

$who = new whois();
$result = $who->checkDomain("flydesign", "nl");

if($result == Whois::AVAILABLE){
echo "Domain available";
} else {
echo "Domain not available";
}

*
*/

class Whois
{

const AVAILABLE = 1;
const TAKEN = 2;
const NO_EXTENSION = 3;
const NO_SERVER = 4;
const ERROR = 5;
const NO_DATA = 6;
const INVALID_NAME = 7;

private $aExtensions;
private $sExecCode;
private $iPort;
private $sResult;

/**
* @desc Create a new whois instance
*/
function __construct(){
$this->iPort = 43;

/**
* Extension array
* Insert new sExtensions in this array, to make more sExtensions available for check
*/
$this->aExtensions = array(
'nl' => array('whois.domain-registry.nl', 'is free'),
'com' => array('whois.crsnic.net', 'No match for'),
'net' => array('whois.crsnic.net', 'No match for'),
'org' => array('whois.publicinterestregistry.net', 'NOT FOUND'),
'co.uk' => array('whois.nic.uk', 'No match'),
'edu' => array('whois.internic.net', 'No match'),
'be' => array('whois.dns.be', 'No such domain'),
'cc' => array('whois.nic.cc', 'No match'),
'ac.cn' => array('whois.cnnic.net.cn', 'no matching record'),
'de' => array('whois.nic.de', 'No entries found'),
'dk' => array('whois.dk-hostmaster.dk', 'No entries found'),
'fr' => array('whois.nic.fr', 'No entries found'),
'it' => array('whois.nic.it', 'No entries found'),
'pl' => array('whois.dns.pl', 'does not exists'),
'se' => array('whois.nic-se.se', 'No data found'),
'info' => array('whois.afilias.net', 'NOT FOUND'),
'biz' => array('whois.nic.biz', 'Not found'),
'ws' => array('whois.nic.ws', 'No match for'),
'gov' => array('whois.nic.gov', 'No match for'),
'tv' => array('whois.internic.net', 'No match for'),
'name' => array('whois.nic.name', 'No match'),
'pro' => array('whois.internic.net', 'No match for'),
'ie' => array('whois.domainregistry.ie', '% There was no match in the IE Domain'),
'us' => array('whois.nic.us', 'Not found:'),
'tk' => array('whois.dot.tk', 'not known'),
'cd' => array('whois.cd', 'No match'),
'aero' => array('whois.information.aero', 'is not registered'),
'gr' => array('whois.ripe.net', 'No entries found'),
'by' => array('whois.ripe.net', 'No entries found'),
'ad' => array('whois.ripe.net', 'No entries found'),
'lv' => array('whois.ripe.net', 'No entries found'),
'eu.lv' => array('whois.biz', 'Not found'),
'bz' => array('mhpwhois1.verisign-grs.net', 'No match'),
'es' => array('whois.ripe.net', 'no entries found'),
'jp' => array('whois.nic.ad.jp', 'No match!!'),
'cl' => array('whois.nic.cl', 'no existe'),
'ag' => array('whois.nic.ag', 'NOT FOUND'),

);
}

/**
* @desc Add a new sExtension to the array (will only apply on the current object)
* @param string $sExtension Name for the sExtension
* @param string $sServer Server for whois data
* @param string $sFreeString Whois data must contain this string if the domain is free
*/
function addExtension($sExtension, $sServer, $sFreeString){
if(!empty($sExtension) AND !empty($sServer) AND

$sExtension)
foreach($this->aExtensions as $sExt => $sServer)
if(substr($sExtension, strlen($sExt)*-1) == $sExt)
$sExtension = $sExt;

if(empty($sDomain) OR empty($sExtension)){
return self::NO_DATA;
}

$sDomainName = $sDomain .".". $sExtension;

if(!preg_match("/^[w.-_]+$/i", $sDomainName)){
return self::INVALID_NAME;
}

if(!isset($this->aExtensions[$sExtension])){
return self::NO_EXTENSION;
}
$aExtensionData = $this->aExtensions[$sExtension];
$sDomainName = escapeshellcmd($sDomainName);

if($bUnix){
if($sExtension "nl"){ $this->sExecCode = "whois -h ".$aExtensionData[0]." "is ".$sDomainName."""; } else { $this->sExecCode = "whois -h ".$aExtensionData[0]." "".$sDomainName."""; } exec($this->sExecCode, $sBuffer); if(!is_array($sBuffer) OR empty($sBuffer)){ return self::ERROR; } $this->sResult = ""; foreach($sBuffer as $sRow){ $this->sResult .= $sRow."n"; } if(stristr($this->sResult, "Connection refused") OR stristr($this->sResult, "No address associated with hostname")){ return self::NO_SERVER; } } else { if($sExtension "nl"){
$this->sExecCode = "is ".$sDomainName;
} else {
$this->sExecCode = $sDomainName;
}
$rSocket = fsockopen($aExtensionData0, $this->iPort);
if(!$rSocket){
return self::NO_SERVER;
}
fputs($rSocket, $this->sExecCode);
$this->sResult = "";
while(!feof($rSocket))
$this->sResult .= fgets($rSocket, 128);
}

if($bRaw){
return $this->sResult;
}

if(stristr($this->sResult, $aExtensionData1)){
return self::AVAILABLE;
} else {
return self::TAKEN;
}

}

/**
* Returns the last server response
*/
public function getLastResponse(){
if(isset($this->sResult))
return $this->sResult;
}
}
?>

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/586042/9f86e6f9bff86461d58ab78f8988051d.png http://posterous.com/users/5AfGsE869oYx Edwin Vlieg winno Edwin Vlieg
Sat, 25 Sep 2004 23:00:09 -0700 Headers already sent http://edwinv.nl/webdevelopment/headers-already-sent http://edwinv.nl/webdevelopment/headers-already-sent

Dit artikel gaat over de veel voorkomende foutmelding:

Warning: Cannot add header information – headers already sent by (output started at index.php:3) in index.php on line 1

Veel mensen vragen zich af wat deze foutmelding betekent en hoe ze hem kunnen oplossen. In dit artikel zal ik de verschillende aspecten van de foutmelding behandelen en proberen een oplossing te vinden.

Wat zegt de foutmelding

De foutmelding zegt dat er output is geweest voor een bepaalde lijn. Op die lijn staat meestal een van deze functies:

Setcookie
Header
Session_start

Een voorbeeld van een fout script kan zijn:

<?php
echo "He, jij hoort hier niet, ik ga je nu redirecten!";
header("Location: redirect.html");
?>

Onduidelijker kan ook, je kunt bijvoorbeeld per ongeluk een spatie of enter voor je php code hebben staan. Dit valt nauwelijks op, maar php ziet dit wel als output.

Hier staat tekst of een spatie
<?php
session_start();
?>

In beide gevallen hebben we iets naar de browser gestuurd voordat we nog iets aan de headers willen veranderen.

Waardoor krijg ik de foutmelding?

Via een web browser werk je met het protocol HTTP. Hierin is vastgelegd hoe een pagina aanroep eruit moet zien en welke gegevens een browser terugkrijgt voor het weergeven van een bepaalde pagina. Een voorbeeld van het opvragen van een pagina kan er zo uitzien:

GET /index.php HTTP/1.1
Host: phpfreakz.nl

Als reactie op deze request krijg je een response terug, die er als volgt uit kan zien:

HTTP/1.1 200 OK
Connection close
Date: Thu, 06 Aug 1998 12:00:15 GMT
Server: Apache/1.3.0 (Unix)
Last-Modified: Mon, 22 Jun 1998
Content-Length: 6821
Content-Type: text/html

<html>
<head>
<title>Pagina</title>

...

Via de bovenstaande functies wil je gegevens aanpassen in de headers van de response. Je kunt bijvoorbeeld instellen hoe je wilt cachen of een locatie aangeven waar de pagina heen verwijst. De opbouw van HTTP vertelt ons dat na de headers een Carriage return moet komen (2x een enter) waarna de eigenlijke inhoud van de pagina komt.
Als je eerst een stukje tekst stuurt, plakt HTTP de 2 enters onder zijn headers en gaat de tekst weergeven. Als jij daarna weer headers wilt toevoegen krijg je een foutmelding, want hij heeft het gedeelte voor de headers al afgesloten en verzonden. PHP zegt heel duidelijk wat er aan de hand is, namelijk:

Warning: Cannot add header information – headers already sent by (output started at index.php:1) in index.php on line 3

Vertalen naar het Nederlands levert op:

Waarschuwing: Kan geen header informatie toevoegen – headers zijn al verzonden (output gestart op index.php:1) in index.php op regel 3

Tussen de haakjes geeft hij zelfs al aan waar je je output hebt staan, heel simpel om dat terug te vinden en op te lossen dus.

Hoe kan ik de foutmelding weg krijgen?

Er zijn 2 mogelijke oplossingen voor het probleem. De beste oplossing is ervoor zorgen dat je geen gegevens verzendt voordat je de bovenstaande functies aanroept. Bij het scripten moet je er dus rekening mee houden dat die functies altijd als eerste behandeld worden, daarna komt de rest pas. Het zijn de zaken die je als eerste moet weten, daarna komt de rest van de pagina inhoud pas. Een kleine moeite en het voorkomt een foutmelding.

Voorbeeld:

<?php
session_start();
echo "En nu pas de teksten";
?>

Mogelijkheid twee is output buffering. Via output control kan je de output naar de browser vasthouden totdat je een bepaald commando geeft. Je kunt dan je hele script door de headers aanpassen zonder foutmeldingen te krijgen. Aan het einde stuur je alle inhoud van de buffer in een keer.

Voorbeeld:

<?php
ob_start();
echo "Een stukje tekst";

setcookie("cookiename", "cookiedata");

ob_end_flush();
?>

Output buffering is eigenlijk meer het probleem omzeilen dan het eigenlijk oplossen. Het is dus zeker aan te raden om tijdens het scripten de eerste oplossing te gebruiken.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/586042/9f86e6f9bff86461d58ab78f8988051d.png http://posterous.com/users/5AfGsE869oYx Edwin Vlieg winno Edwin Vlieg