Index: classbuilder.c
===================================================================
--- classbuilder.c	(revision 4881)
+++ classbuilder.c	(working copy)
@@ -27,8 +27,7 @@
 
 #include "geanyplugin.h"
 
-
-GeanyData		*geany_data;
+GeanyData	*geany_data;
 GeanyFunctions	*geany_functions;
 
 
@@ -44,7 +43,8 @@
 enum
 {
 	GEANY_CLASS_TYPE_CPP,
-	GEANY_CLASS_TYPE_GTK
+	GEANY_CLASS_TYPE_GTK,
+	GEANY_CLASS_TYPE_PHP
 };
 
 typedef struct _ClassInfo	ClassInfo;
@@ -67,6 +67,11 @@
 	gchar *constructor_impl;
 	gchar *destructor_impl;
 	gchar *gtk_destructor_registration;
+	// These are needed only for PHP classes
+	gchar *namespace_decl;
+	gchar *implements_decl;
+	gchar *abstract_decl;
+	gchar *singleton_impl;
 };
 
 typedef struct _CreateClassDialog
@@ -83,6 +88,11 @@
 	GtkWidget *create_constructor_box;
 	GtkWidget *create_destructor_box;
 	GtkWidget *gtk_constructor_type_entry;
+	// These are needed only for PHP classes
+	GtkWidget *class_namespace_entry;
+	GtkWidget *class_implements_entry;
+	GtkWidget *create_isabstract_box;
+	GtkWidget *create_issingleton_box;
 } CreateClassDialog;
 
 
@@ -184,7 +194,19 @@
 {constructor_impl}\n\
 ";
 
+static const gchar templates_php_class_source[] = "<?php\n\
+{fileheader}\n\
+{namespace_decl}\n\
+{base_include}\n\
+{abstract_decl}class {class_name}{base_decl}{implements_decl}\n\{\n\
+{singleton_impl}\
+{constructor_impl}\
+{destructor_impl}\n\
+	// ...\n\n\
+}\n\
+";
 
+
 static void cc_dlg_on_set_sensitive_toggled(GtkWidget *toggle_button, GtkWidget *target_widget);
 static void cc_dlg_on_class_name_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg);
 static void cc_dlg_on_base_name_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg);
@@ -298,6 +320,21 @@
 			utils_string_replace_all(template, "{gtk_destructor_registration}",
 					class_info->gtk_destructor_registration);
 			break;
+    
+		case GEANY_CLASS_TYPE_PHP:
+			fileheader = templates_get_template_fileheader(GEANY_FILETYPES_PHP, class_info->source);
+			template = g_string_new(templates_php_class_source);
+			utils_string_replace_all(template, "{fileheader}", fileheader);
+			utils_string_replace_all(template, "{namespace_decl}", class_info->namespace_decl);
+			utils_string_replace_all(template, "{base_include}", class_info->base_include);
+			utils_string_replace_all(template, "{abstract_decl}", class_info->abstract_decl);
+			utils_string_replace_all(template, "{class_name}", class_info->class_name);
+			utils_string_replace_all(template, "{base_decl}", class_info->base_decl);
+			utils_string_replace_all(template, "{implements_decl}", class_info->implements_decl);
+			utils_string_replace_all(template, "{constructor_impl}", class_info->constructor_impl);
+			utils_string_replace_all(template, "{destructor_impl}", class_info->destructor_impl);
+			utils_string_replace_all(template, "{singleton_impl}", class_info->singleton_impl);
+			break;
 	}
 
 	g_free(fileheader);
@@ -331,7 +368,25 @@
 	g_signal_connect_swapped(cc_dlg->dialog, "destroy", G_CALLBACK(g_free), (gpointer)cc_dlg);
 
 	main_box = ui_dialog_vbox_new(GTK_DIALOG(cc_dlg->dialog));
-
+	
+	if (type == GEANY_CLASS_TYPE_PHP)
+	{
+		frame = ui_frame_new_with_alignment(_("Namespace"), &align);
+		gtk_container_add(GTK_CONTAINER(main_box), frame);
+		
+		vbox = gtk_vbox_new(FALSE, 10);
+		gtk_container_add(GTK_CONTAINER(align), vbox);
+		
+		hbox = gtk_hbox_new(FALSE, 10);
+		gtk_container_add(GTK_CONTAINER(vbox), hbox);
+		
+		label = gtk_label_new(_("Namespace:"));
+		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+		
+		cc_dlg->class_namespace_entry = gtk_entry_new();
+		gtk_box_pack_start(GTK_BOX(hbox), cc_dlg->class_namespace_entry, TRUE, TRUE, 0);
+	}
+	
 	frame = ui_frame_new_with_alignment(_("Class"), &align);
 	gtk_container_add(GTK_CONTAINER(main_box), frame);
 
@@ -348,25 +403,28 @@
 	gtk_box_pack_start(GTK_BOX(hbox), cc_dlg->class_name_entry, TRUE, TRUE, 0);
 	g_signal_connect(cc_dlg->class_name_entry, "changed",
 			G_CALLBACK(cc_dlg_on_class_name_entry_changed), cc_dlg);
-
+	
+	if (type != GEANY_CLASS_TYPE_PHP) 
+	{
+		hbox = gtk_hbox_new(FALSE, 10);
+		gtk_container_add(GTK_CONTAINER(vbox), hbox);
+		
+		label = gtk_label_new(_("Header file:"));
+		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+		
+		cc_dlg->header_entry = gtk_entry_new();
+		gtk_container_add(GTK_CONTAINER(hbox), cc_dlg->header_entry);
+	}
+	
 	hbox = gtk_hbox_new(FALSE, 10);
 	gtk_container_add(GTK_CONTAINER(vbox), hbox);
-
-	label = gtk_label_new(_("Header file:"));
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
-	cc_dlg->header_entry = gtk_entry_new();
-	gtk_container_add(GTK_CONTAINER(hbox), cc_dlg->header_entry);
-
-	hbox = gtk_hbox_new(FALSE, 10);
-	gtk_container_add(GTK_CONTAINER(vbox), hbox);
-
+	
 	label = gtk_label_new(_("Source file:"));
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
+	
 	cc_dlg->source_entry = gtk_entry_new();
 	gtk_container_add(GTK_CONTAINER(hbox), cc_dlg->source_entry);
-
+	
 	frame = ui_frame_new_with_alignment(_("Inheritance"), &align);
 	gtk_container_add(GTK_CONTAINER(main_box), frame);
 
@@ -387,20 +445,26 @@
 			G_CALLBACK(cc_dlg_on_base_name_entry_changed), (gpointer)cc_dlg);
 
 	hbox = gtk_hbox_new(FALSE, 10);
-	gtk_container_add(GTK_CONTAINER(vbox), hbox);
-
-	label = gtk_label_new(_("Base header:"));
+ 	gtk_container_add(GTK_CONTAINER(vbox), hbox);
+	
+	if (type == GEANY_CLASS_TYPE_PHP)
+		label = gtk_label_new(_("Base source:"));
+	else
+		label = gtk_label_new(_("Base header:"));
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
+	
 	cc_dlg->base_header_entry = gtk_entry_new();
 	if (type == GEANY_CLASS_TYPE_GTK)
 		gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_header_entry), "glib-object.h");
 	gtk_container_add(GTK_CONTAINER(hbox), cc_dlg->base_header_entry);
-
-	cc_dlg->base_header_global_box = gtk_check_button_new_with_label(_("Global"));
-	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cc_dlg->base_header_global_box), TRUE);
-	gtk_box_pack_end(GTK_BOX(hbox), cc_dlg->base_header_global_box, FALSE, FALSE, 0);
-
+	
+	if (type != GEANY_CLASS_TYPE_PHP)
+	{
+		cc_dlg->base_header_global_box = gtk_check_button_new_with_label(_("Global"));
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cc_dlg->base_header_global_box), TRUE);
+		gtk_box_pack_end(GTK_BOX(hbox), cc_dlg->base_header_global_box, FALSE, FALSE, 0);
+	}
+	
 	if (type == GEANY_CLASS_TYPE_GTK)
 	{
 		hbox = gtk_hbox_new(FALSE, 10);
@@ -413,7 +477,19 @@
 		gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_gtype_entry), "G_TYPE_OBJECT");
 		gtk_container_add(GTK_CONTAINER(hbox), cc_dlg->base_gtype_entry);
 	}
-
+	
+	if (type == GEANY_CLASS_TYPE_PHP)
+	{
+		hbox = gtk_hbox_new(FALSE, 10);
+		gtk_container_add(GTK_CONTAINER(vbox), hbox);
+		
+		label = gtk_label_new(_("Implements:"));
+		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+		
+		cc_dlg->class_implements_entry = gtk_entry_new();
+		gtk_container_add(GTK_CONTAINER(hbox), cc_dlg->class_implements_entry);
+	}
+	
 	frame = ui_frame_new_with_alignment(_("Options"), &align);
 	gtk_container_add(GTK_CONTAINER(main_box), frame);
 
@@ -429,7 +505,19 @@
 
 	cc_dlg->create_destructor_box = gtk_check_button_new_with_label(_("Create destructor"));
 	gtk_container_add(GTK_CONTAINER(hbox), cc_dlg->create_destructor_box);
-
+	
+	if (type == GEANY_CLASS_TYPE_PHP)
+	{
+		hbox = gtk_hbox_new(FALSE, 10);
+		gtk_container_add(GTK_CONTAINER(vbox), hbox);
+		
+		cc_dlg->create_isabstract_box = gtk_check_button_new_with_label(_("Is abstract"));
+		gtk_container_add(GTK_CONTAINER(hbox), cc_dlg->create_isabstract_box);
+		
+		cc_dlg->create_issingleton_box = gtk_check_button_new_with_label(_("Is singleton"));
+		gtk_container_add(GTK_CONTAINER(hbox), cc_dlg->create_issingleton_box);
+	}
+	
 	if (type == GEANY_CLASS_TYPE_GTK)
 	{
 		hbox = gtk_hbox_new(FALSE, 10);
@@ -472,6 +560,7 @@
 
 static void cc_dlg_on_class_name_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg)
 {
+	gchar *class_name;
 	gchar *class_name_down;
 	gchar *class_header;
 	gchar *class_source;
@@ -480,16 +569,20 @@
 	g_return_if_fail(GTK_IS_ENTRY(entry));
 	g_return_if_fail(cc_dlg != NULL);
 
-	class_name_down = g_ascii_strdown(gtk_entry_get_text(GTK_ENTRY(entry)), -1);
+	class_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+	class_name_down = g_ascii_strdown(class_name, -1);
 	class_header = g_strconcat(class_name_down, ".h", NULL);
 	if (cc_dlg->class_type == GEANY_CLASS_TYPE_CPP)
 		class_source = g_strconcat(class_name_down, ".cpp", NULL);
+	else if (cc_dlg->class_type == GEANY_CLASS_TYPE_PHP)
+		class_source = g_strconcat(class_name, ".php", NULL);
 	else
 		class_source = g_strconcat(class_name_down, ".c", NULL);
 
 	gtk_entry_set_text(GTK_ENTRY(cc_dlg->header_entry), class_header);
 	gtk_entry_set_text(GTK_ENTRY(cc_dlg->source_entry), class_source);
-
+	
+	g_free(class_name);
 	g_free(class_name_down);
 	g_free(class_header);
 	g_free(class_source);
@@ -525,7 +618,7 @@
 	g_return_if_fail(entry != NULL);
 	g_return_if_fail(GTK_IS_ENTRY(entry));
 	g_return_if_fail(cc_dlg != NULL);
-
+  
 	base_name_splitted = str_case_split(gtk_entry_get_text(GTK_ENTRY(entry)), '_');
 	if (! g_ascii_strncasecmp(gtk_entry_get_text(GTK_ENTRY(entry)), "gtk", 3))
 		/*tmp = g_strconcat("gtk/", gtk_entry_get_text(GTK_ENTRY(entry)), ".h", NULL);*/
@@ -533,9 +626,16 @@
 		tmp = g_strdup("gtk/gtk.h");
 	else if (utils_str_equal(gtk_entry_get_text(GTK_ENTRY(entry)), "GObject"))
 		tmp = g_strdup("glib-object.h");
+	else if (cc_dlg->class_type == GEANY_CLASS_TYPE_PHP)
+		tmp = g_strconcat(gtk_entry_get_text(GTK_ENTRY(entry)), ".php", NULL);
 	else
 		tmp = g_strconcat(gtk_entry_get_text(GTK_ENTRY(entry)), ".h", NULL);
-	base_header = g_ascii_strdown(tmp, -1);
+	
+	if (cc_dlg->class_type == GEANY_CLASS_TYPE_PHP)
+		base_header = g_strdup(tmp);
+	else
+		base_header = g_ascii_strdown(tmp, -1);
+	
 	g_free(tmp);
 
 	gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_header_entry), base_header);
@@ -584,12 +684,21 @@
 	if (! utils_str_equal(gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_name_entry)), ""))
 	{
 		class_info->base_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_name_entry)));
-		class_info->base_include = g_strdup_printf("\n#include %c%s%c\n",
-			gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->base_header_global_box)) ?
+		if (class_info->type != GEANY_CLASS_TYPE_PHP) 
+		{
+			class_info->base_include = g_strdup_printf("\n#include %c%s%c\n",
+				gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->base_header_global_box)) ?
 				'<' : '\"',
-			gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_header_entry)),
-			gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->base_header_global_box)) ?
+				gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_header_entry)),
+				gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->base_header_global_box)) ?
 				'>' : '\"');
+		}
+		else
+		{
+			class_info->base_include = g_strdup_printf("\nrequire_once \"%s\";\n", 
+				gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_header_entry)));
+				class_info->base_decl = g_strdup_printf(" extends %s", class_info->base_name);
+		}
 	}
 	else
 	{
@@ -697,8 +806,110 @@
 			}
 			break;
 		}
+		case GEANY_CLASS_TYPE_PHP:
+		{
+			gchar *tmp;
+			
+			class_info->source = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->source_entry)));
+			
+			tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_namespace_entry)));
+			if (! utils_str_equal(tmp, ""))
+				class_info->namespace_decl = g_strdup_printf("namespace %s;", tmp);
+			else
+				class_info->namespace_decl = g_strdup("");
+			
+			tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_implements_entry)));
+			if (! utils_str_equal(tmp, ""))
+				class_info->implements_decl = g_strdup_printf(" implements %s", tmp);
+			else
+				class_info->implements_decl = g_strdup("");
+			g_free(tmp);
+			
+			if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_constructor_box)) && 
+			    ! gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_isabstract_box)))
+			{
+				class_info->constructor_impl = g_strdup_printf("\n"
+					"\t/**\n"
+					"\t * Constructor of class %s.\n"
+					"\t *\n"
+					"\t * @return void\n"
+					"\t */\n"
+					"\tpublic function __construct()\n"
+					"\t{\n"
+					"\t\t// ...\n"
+					"\t}\n",
+					class_info->class_name);
+			}
+			else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_constructor_box)) && 
+			         gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_isabstract_box)))
+			{
+				class_info->constructor_impl = g_strdup_printf("\n"
+					"\t/**\n"
+					"\t * Constructor of class %s.\n"
+					"\t *\n"
+					"\t * @return void\n"
+					"\t */\n"
+					"\tprotected function __construct()\n"
+					"\t{\n"
+					"\t\t// ...\n"
+					"\t}\n",
+					class_info->class_name);
+			}
+			else 
+				class_info->constructor_impl = g_strdup("");
+			
+			if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_destructor_box)))
+			{
+				class_info->destructor_impl = g_strdup_printf("\n"
+					"\t/**\n"
+					"\t * Destructor of class %s.\n"
+					"\t *\n"
+					"\t * @return void\n"
+					"\t */\n"
+					"\tpublic function __destruct()\n"
+					"\t{\n"
+					"\t\t// ...\n"
+					"\t}\n",
+					class_info->class_name);
+			}
+			else
+				class_info->destructor_impl = g_strdup("");
+			
+			if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_isabstract_box)))
+				class_info->abstract_decl = g_strdup("abstract ");
+			else 
+				class_info->abstract_decl = g_strdup("");
+			
+			if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_issingleton_box)))
+			{
+				class_info->singleton_impl = g_strdup_printf("\n"
+					"\t/**\n"
+					"\t * Holds instance of self.\n"
+					"\t * \n"
+					"\t * @var %s\n"
+					"\t */\n"
+					"\tprotected static $kInstance = null;\n\n"
+					"\t/**\n"
+					"\t * Returns instance of self.\n"
+					"\t * \n"
+					"\t * @return %s\n"
+					"\t */\n"
+					"\tpublic static function getInstance() {\n"
+					"\t\tif(!(self::$kInstance instanceof %s)) {\n"
+					"\t\t\tself::$kInstance = new self();\n"
+					"\t\t}\n"
+					"\t\treturn self::$kInstance;\n"
+					"\t}\n",
+					class_info->class_name, 
+					class_info->class_name, 
+					class_info->class_name);
+			}
+			else
+				class_info->singleton_impl = g_strdup("");
+			break;
+		}
 	}
-
+	
 	/* only create the files if the filename is not empty */
 	if (! utils_str_equal(class_info->source, ""))
 	{
@@ -708,7 +919,8 @@
 		g_free(text);
 	}
 
-	if (! utils_str_equal(class_info->header, ""))
+	if (! utils_str_equal(class_info->header, "") && 
+	      class_info->type != GEANY_CLASS_TYPE_PHP)
 	{
 		text = get_template_class_header(class_info);
 		doc = document_new_file(class_info->header, NULL, NULL);
@@ -721,7 +933,9 @@
 		class_info->header, class_info->header_guard, class_info->source, class_info->base_decl,
 		class_info->constructor_decl, class_info->constructor_impl,
 		class_info->gtk_destructor_registration, class_info->destructor_decl,
-		class_info->destructor_impl, class_info->base_gtype, class_info, NULL);
+		class_info->destructor_impl, class_info->base_gtype, 
+		class_info->namespace_decl, class_info->implements_decl, 
+		class_info->abstract_decl, class_info->singleton_impl, class_info, NULL);
 	return TRUE;
 }
 
@@ -742,12 +956,21 @@
 }
 
 
+static void 
+on_menu_create_php_class_activate	(GtkMenuItem	*menuitem,
+					 gpointer	user_data)
+{
+	show_dialog_create_class(GEANY_CLASS_TYPE_PHP);
+}
+
+
 void plugin_init(GeanyData *data)
 {
 	GtkWidget *menu_create_class1;
 	GtkWidget *menu_create_class1_menu;
 	GtkWidget *menu_create_cpp_class;
 	GtkWidget *menu_create_gtk_class;
+	GtkWidget *menu_create_php_class;
 
 	menu_create_class1 = ui_image_menu_item_new (GTK_STOCK_ADD, _("Create Cla_ss"));
 	gtk_container_add (GTK_CONTAINER (geany->main_widgets->tools_menu), menu_create_class1);
@@ -760,6 +983,9 @@
 
 	menu_create_gtk_class = gtk_menu_item_new_with_mnemonic (_("_GTK+ Class"));
 	gtk_container_add (GTK_CONTAINER (menu_create_class1_menu), menu_create_gtk_class);
+	
+	menu_create_php_class = gtk_menu_item_new_with_mnemonic (_("_PHP Class"));
+	gtk_container_add (GTK_CONTAINER (menu_create_class1_menu), menu_create_php_class);
 
 	g_signal_connect(menu_create_cpp_class, "activate",
 		G_CALLBACK (on_menu_create_cpp_class_activate),
@@ -767,6 +993,9 @@
 	g_signal_connect(menu_create_gtk_class, "activate",
 		G_CALLBACK (on_menu_create_gtk_class_activate),
 		NULL);
+	g_signal_connect(menu_create_php_class, "activate",
+		G_CALLBACK (on_menu_create_php_class_activate), 
+		NULL);
 
 	gtk_widget_show_all(menu_create_class1);
 
