-
Notifications
You must be signed in to change notification settings - Fork 0
Internal API
The aim of this document is to explain the functions used internally in the development of the C extension.
The way the code is written is not the usual, if you compare the code of Phalcon with other extensions you'll see a huge diference, this has several objectives:
- Don't repeat yourself, if PHP has a functionality already developed, why do not simply use it?
- Writing the code more in a PHP style, this will help to PHP developers to introspect easily understanding the internals of PHP objects. Reflection
- Help the Phalcon user to obtain more human backtraces
- Avoid dealing with low-level issues, whenever possible.
We, the creators of Phalcon, are mainly PHP programmers, we are lazy and do not want to deal 100% of the time with low-level details and segmentation faults. Moreover we want a fast and stable framework. For this reason we have created the Phalcon API. The use of this API helps us to write C code in a PHP style. We have developed a number of functions to help the programmer to write code more interoperable with PHP in a easier way.
Phalcon API is based on the Zend API, but we have added more features to facilitate the work. Phalcon is a very large project, frameworks need to be developed and improved every day, the Phalcon API helps us write code in C more stable and familiar to PHP developers.
Zvals: Most variables used in the framework are Zvals. Each value within a PHP application is a zval. The Zvals are polymorphic structures, ie a zval can have any value (string, long, double, array, etc.). Inspecting some code you will see that most variables are declared Zvals. PHP has scalar data types (string, null, bool, long and double) and non-scalar data types (arrays and objects). There is a way to assign a value the zval according to the data type:
PHALCON_INIT_VAR(name);
ZVAL_STRING(name, "Sonny", 1);
PHALCON_INIT_VAR(number);
ZVAL_LONG(number, 12000);
PHALCON_INIT_VAR(price);
ZVAL_DOUBLE(price, 15.50);
PHALCON_INIT_VAR(nothing);
ZVAL_NULL(nothing);
PHALCON_INIT_VAR(is_alive);
ZVAL_BOOL(is_alive, false);
Zend Class Entries: Another common structure we used is the zend class entry. That structure help us to describe a class, its name, method, default properties, etc.
//Get the class entry
class_entry = Z_OBJCE_P(this_ptr);
//Print the class name
fprintf(stdout, "%s", class_entry->name);
As you may know, the memory management in C is all manual. Within a PHP extension, we can leverage the use of the Zend Memory Manager, however management remains manual.
To help in the creation of Phalcon, we have created a memory manager that basically track every variable allocated in order to free it before exit the active method:
For example:
PHP_METHOD(Phalcon_Some_Component, sayHello){
zval *greeting = NULL;
PHALCON_MM_GROW();
PHALCON_INIT_VAR(gretting);
ZVAL_STRING(greeting, "Hello!", 1);
PHALCON_MM_RESTORE();
}
PHALCON_MM_GROW starts a memory frame in the memory manager, then PHALCON_INIT_VAR allocates memory for the variable greeting, before exit the method we call PHALCON_MM_RESTORE, this releases all the memory allocated from the last call to PHALCON_MM_GROW.
By this way, we can be sure that all the memory allocated will be freed, avoiding memory leaks. Let's pretend that we used 10-15 variables, releasing manually the memory of them can be tedious.
As noted above, all variables must be initialized before use. Even, it should be reset again when we change its value, for example:
//Declare the variable
zval some_number = NULL;
//Initialize the variable and assign it a string value
PHALCON_INIT_VAR(some_number);
ZVAL_STRING(some_number, "one hundred");
//Reinitialize the variable and change its value to long
PHALCON_INIT_VAR(some_number);
ZVAL_LONG(some_number, 100);
When declaring the variables is important to initialize them to NULL. By doing this, PHALCON_INIT_VAR will know if the variable needs memory or it already have memory allocated.
PHP is a dynamic language, we can do almost any operation between two variables, regardless of type. Sometimes we do not know exactly the type of data that have the variables, using the Zend API we can do operations between them seamlessly:
//First variable is string but it's a numeric string
PHALCON_INIT_VAR(first_var);
ZVAL_STRING(first_var, "100.10", 1);
//Second variable is long
PHALCON_INIT_VAR(second_var);
ZVAL_LONG(second_var, 150);
//add_function will make the neccesary convertions to produce the addition
PHALCON_INIT_VAR(result);
add_function(result, first_var, second_var);
Concatenation is one of the most common operations we do in PHP. However using the Zend API can be tedious when concatenate many values, for example:
// The following concatention using just Zend API:
//
// $month = "July"; $day = 1;
// $today = "Today is ".$month." ".$day;
PHALCON_INIT_VAR(month);
ZVAL_STRING(month, "2012", 1);
PHALCON_INIT_VAR(day);
ZVAL_LONG(day, 1);
PHALCON_INIT_VAR(today_is);
ZVAL_STRING(today_is, "Today is", 1);
PHALCON_INIT_VAR(first_part);
concat_function(first_part, today_is, month);
PHALCON_INIT_VAR(space);
ZVAL_STRING(space, " ", 1);
PHALCON_INIT_VAR(final_part);
concat_function(final_part, space, day);
Another way to do that is use sprintf, in this case, you need to be completely sure that the variables have all string types:
char *final_string;
zval *final;
PHALCON_INIT_VAR(month);
ZVAL_STRING(month, "2012", 1);
PHALCON_INIT_VAR(day);
ZVAL_STRING(day, "1", 1);
final_string = emalloc(sizeof(char)*(Z_STRLEN_P(month)+Z_STRLEN_P(day)+24)));
sprintf(final_string, "Today is %s %s", Z_STRVAL_P(month), Z_STRVAL_P(day));
PHALCON_INIT_VAR(final);
ZVAL_STRING(final, final_string, 0);
It's short, but if some of your variables aren't string you will get a segmentation fault or an unexpected behavior.
To help to solve this problem, we have created a set of macros to concatenate zvals and strings:
PHALCON_INIT_VAR(month);
ZVAL_STRING(month, "2012", 1);
PHALCON_INIT_VAR(day);
ZVAL_STRING(day, "1", 1);
PHALCON_INIT_VAR(result);
PHALCON_CONCAT_SVSV(result, "Today is", month, " ", day);
Other examples:
PHALCON_CONCAT_VV(result, month, day); //July1
PHALCON_CONCAT_VSV(result, month, ", ", day); //July, 1
PHALCON_CONCAT_SVSV(result, "Today is", month, " ", day); //July 1
PHALCON_CONCAT_SVSVSV(result, "Today is", month, " ", day, ", ", year); //July 1, 2012
S=String and V=Zval, just put the S and V to get the right concatenation macro. Easy, no?
Although the Zend API, and provides various functions for working with arrays, with Phalcon API we added other. Specifically helping to maintain the reference counting correctly:
//Declare the variable
zval fruits = NULL;
PHALCON_INIT_VAR(fruits);
array_init(fruits);
//Adding items to the array
add_next_index_stringl(fruits, SL("apple"), 1);
add_next_index_stringl(fruits, SL("orange"), 1);
add_next_index_stringl(fruits, SL("lemon"), 1);
add_next_index_stringl(fruits, SL("banana"), 1);
//Get the first item in the array $fruits[0]
PHALCON_INIT_VAR(first_item);
phalcon_array_fetch_long(&first_item, fruits, 0, PH_NOISY_CC);
Mixing both string and number indexes:
//Let's create the following array using the Phalcon API
//$fruits = array(1, null, false, "some string", 15.20, "my-index" => "another string");
PHALCON_INIT_VAR(fruits);
array_init(fruits);
add_next_index_long(fruits, 1);
add_next_index_null(fruits);
add_next_index_bool(fruits, 0);
add_next_index_stringl(fruits, SL("some string"), 1);
add_next_index_double(fruits, 15.2);
add_assoc_stringl_ex(fruits, SL("my-index")+1, SL("another string"), 1);
//Updating an existing index $fruits[2] = "other value";
phalcon_array_update_long_string(&fruits, 2, SL("other value"), PH_SEPARATE TSRMLS_CC);
//Removing an existing index unset($fruits[1]);
phalcon_array_unset_long(fruits, 1);
//Removing an existing index unset($fruits["my-index"]);
phalcon_array_unset_string(fruits, SL("my-index")+1);
Instantiate objects of the framework classes is easy:
PHALCON_INIT_VAR(route);
object_init_ex(route, phalcon_mvc_router_route_ce);
PHALCON_INIT_VAR(pattern);
ZVAL_STRING(pattern, "#^/([a-zA-Z0-9\\_]+)[/]{0,1}$#", 1);
PHALCON_CALL_METHOD_PARAMS_1_NORETURN(route, "__construct", pattern, PH_CHECK);
The above code is the same as doing in PHP:
$route = new Phalcon\Mvc\Router\Route("#^/([a-zA-Z0-9\\_]+)[/]{0,1}$#");
When you throw an exception using the Phalcon API, the current flow of execution will be stopped, returning to the last PHP code block when a Phalcon method where called.
There are two ways to throw exceptions, the first, when the exception object only receives a string as parameter:
PHALCON_THROW_EXCEPTION_STR(phalcon_exception_ce, "Hey this is an exception");
Or building the exception manually and then throwing it:
PHALCON_INIT_VAR(exception_message);
PHALCON_CONCAT_SVS(exception_message, "Unable to insert into ", table, " without data");
PHALCON_INIT_VAR(exception);
object_init_ex(exception, phalcon_db_exception_ce);
//The exception constructor must be manually called
PHALCON_CALL_METHOD_PARAMS_1_NORETURN(exception, "__construct", exception_message, PH_CHECK);
phalcon_throw_exception(exception TSRMLS_CC);
return;