Writing PHP Extensions – Part 6 Adding a Class to your Extension

See Part 5 Here

We are now going to look at how to create a C++ class to encapsulate our extension code. This is useful if you want to group similar functions or methods in one structure.

In the sample code below, we will setup a class to display a Yearly calendar as a HTML table.  The helloworld extension will define a class called Cal which will have one callable method getYearly(<year>) .  The year passed as argument is what will be printed. Obviously this extension is only useful when called in a web page, although it can be used in a command line php also.

WRITING CLASSES

Creating a C++ class in your extension main.cpp requires small changes to the standard C++ code .

  1. All classes must be derived from Php::Base
  2. Any class method which will be callable from PHP should use Php::Value for returning values and Php::Parameters for passing arguments.
  3. Any method which are used only internally within the class need not follow any special PHP considerations.
  4. In the *get_module() function we need to declare our class and its public methods and then use extension.add() to add the declared class.

THE CALENDAR CODE

#include <phpcpp.h>
#include <iostream>
#include <iomanip>
#include <string>

using namespace std;

class Cal : public Php::Base {

	public:
		Cal(void);
		Php::Value getYearly(Php::Parameters &params);

	protected:
		int mYear;
		string mMonths[13];
		string mWeekDays [8];
		int mDays [13];
		int firstDayOfMonth(int month, int year);


};

/**
 * Constructor - initialize data 
 *
 */
Cal::Cal(void) {
		mMonths[0] = "";	// month starts from 1
		mMonths[1] = "January";
		mMonths[2] = "February";
		mMonths[3] = "March";
		mMonths[4] = "April";
		mMonths[5] = "May";
		mMonths[6] = "June";
		mMonths[7] = "July";
		mMonths[8] = "August";
		mMonths[9] = "September";
		mMonths[10] = "October";
		mMonths[11] = "November";
		mMonths[12] = "December";

		mDays[0] =0;
		mDays[1] = 31;
		mDays[2] = 28;
		mDays[3] = 31;
		mDays[4] = 30;
		mDays[5] = 31;
		mDays[6] = 30;
		mDays[7] = 31;
		mDays[8] = 31;
		mDays[9] = 30;
		mDays[10] = 31;
		mDays[11] = 30;
		mDays[12] = 31;

		mWeekDays[0] = "";
		mWeekDays[1] = "Sun";
		mWeekDays[2] = "Mon";
		mWeekDays[3] = "Tue";
		mWeekDays[4] = "Wed";
		mWeekDays[5] = "Thu";
		mWeekDays[6] = "Fri";
		mWeekDays[7] = "Sat";


}

/**
 * Using a standard c++ algorithm to find day of week for a date
 * Parameters:
 *  month - month number
 *  year - year
 * Returns:
 *	dow = day of week, sun =0, mon=1 etc.
 */
int Cal::firstDayOfMonth(int month, int year) {
	int dow = 0;
	int day = 1;

    int t[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
    year -= month < 3;
    dow = ( year + year/4 - year/100 + year/400 + t[month-1] + day) % 7;

	return dow;
}

/**
 * Print yearly calendar as html <table>
 * Each row will have 7 td columns for the days of the week
 * Parameters:
 *  year - year to print for
 */
Php::Value  Cal::getYearly(Php::Parameters &params) {
	int year = 0;

	year = (int) params[0];

	string output = "";

	mYear = year;
	if (mYear < 1900 || mYear > 2100) {
		std::cout << "Year is " << mYear << ". Year must be between 1900 and 2100" << endl;
		return output;
	}

	// check for leap year
	if ((mYear % 4 == 0 && mYear % 100 != 0) || (mYear % 400 == 0))
		mDays[2] = 29;

	output.append("<table width=100%% cellpadding=2 cellspacing=2 border=1>");

	for (int i =1; i <=12; i ++) {
		
		output.append("<tr valign=top>");
		output.append("<td colspan=7 align=center><b>");
		output.append(mMonths[i]);
		output.append(", ");
		output.append(to_string(mYear));
		output.append("</td></tr>");

		//print weekday labels
		output.append("<tr valign=top>");
		for (int j = 1; j <= 7; j++) {
			output.append("<td><font color=red>");
			output.append(mWeekDays[j]);
			output.append("</font></td>");
		}
		output.append("</tr>");

		// print days
		int daysPrinted = 0;
		output.append("<tr valing=top>");
		for (int j = 1; j <= mDays[i]; j++) {
			// move display head to the correct day of week for 1st
			if (j == 1) {
				int dow = firstDayOfMonth(i, mYear);
				for (int spaces=0; spaces < dow; spaces++) {
					output.append("<td>&nbsp;</td>");
					daysPrinted ++;
				}
			}

			output.append("<td>");
			output.append(to_string(j));
			output.append("</td>");
			daysPrinted ++;

			if (daysPrinted % 7 == 0) {
				output.append("</tr>");
				// start next row if more days are left
				if (j < mDays[i])
					output.append("<tr valign=top>");
				daysPrinted = 0;
			}
		} // for (int j = 1; j <= mDays[i]; j++) 


		// close pending <tr>
		if (daysPrinted > 0)
			output.append("</tr>");
	
	} // 	for (int i =1; i <=12; i ++) 
	output.append("</table>");		

	return output;
}

/**
 *  tell the compiler that the get_module is a pure C function
 */
extern "C" {
    
    /**
     *  Function that is called by PHP right after the PHP process
     *  has started, and that returns an address of an internal PHP
     *  strucure with all the details and features of your extension
     *
     *  @return void*   a pointer to an address that is understood by PHP
     */
    PHPCPP_EXPORT void *get_module() 
    {
        // static(!) Php::Extension object that should stay in memory
        // for the entire duration of the process (that's why it's static)
        static Php::Extension extension("helloworld", "1.0");
        
        // @todo    add your own functions, classes, namespaces to the extension

		Php::Class<Cal> cal("Cal");
		cal.method("getYearly", &Cal::getYearly, 
				{Php::ByVal("year", Php::Type::Numeric, true)}
		);
		extension.add(std::move(cal));

        // return the extension
        return extension;
    }
}

Follow the normal steps to build the extension as given in the previous posts:

  • Run make
  • Run sudo make install
  • Run sudo ./webenable

The PHP code to test the extension is just a few lines of code:

 

$cal = new Cal();
$calendar = $cal->getYearly(2015);
echo($calendar);

The web page output is shown below:

Screenshot from 2015-09-13 10:22:00

1 Trackback / Pingback

  1. Writing PHP Extensions – Part 5 Web-enabling your extension | Truelogic Blog

Leave a Reply

Your email address will not be published.


*


This site uses Akismet to reduce spam. Learn how your comment data is processed.