PHP Extension: Working with class objects - c

Let's say I'm creating a PHP 7 extension (in C, NOT C++ and I do not want answers that talk about PHP-CPP at all) and I want to make a php object like Dog and give it variables and functions in the extension itself.
Lets say we have a PHP class like this...
class Dog {
public name;
public age;
private color;
private function play_ball() {
echo $this->name . " is playing with a ball!";
}
public function get_color() {
$this->play_ball();
return $this->color;
}
}
How would someone do this in an extension written in C? Is this even possible?

I will give you an example of the class, but I'll drop this link to github because I think it'll give you a starting point from where you can get the rest of the information about how to create an extension
https://github.com/twigphp/Twig/blob/1.x/ext/twig/twig.c
And here is the class definition code, it's quite verbose and you can use macros and function calls to reduce the amount of boilerplate code, but I'm just trying to explain how to do it, not show you the ultimate, best way to do everything.
Please Note: I didnt compile this code, although I'm sure it's 99% accurate, maybe you'll have to fix up some small issues, just ask me if you have doubts.
// I won't include this file, but just look at the twig.c extension source code
// It's mostly boilerplate and you just copy and paste what you need
#include "php_myanimals.h"
// This is the "class entry" php will use to define your class
zend_class_entry *dog_ce;
#define DECLARE_MEMBER(type,name,value,access) \
type(dog_ce, name, strlen(name), value, access TSRMLS_CC)
#define DECLARE_STRING(name,value,access) \
DECLARE_MEMBER(zend_declare_property_string,name,value,access)
#define DECLARE_LONG(name,value,access) \
DECLARE_MEMBER(zend_declare_property_long,name,value,access)
#define SET_PARAM(name,value) \
zend_update_property(dog_ce, this_ptr, name, strlen(name), value TSRMLS_CC)
#define GET_PARAM(name) \
zend_read_property(dog_ce, this_ptr, name, strlen(name), 1 TSRMLS_CC)
/* {{{ Method: Dog::__construct() */
PHP_METHOD(dog, __construct)
{
zval *name = NULL;
zval *colour = NULL;
// First look in the parameter list for a server string
// The | means that all parameters afterwards, are optional
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &name, &colour) == FAILURE)
{
EX_INVALID_ARG("MyAnimals\\Dog::__construct(string name, [string colour]), parameters were not valid");
}
SET_PARAM("name",name);
SET_PARAM("colour",colour);
}
/* }}} */
/* {{{ Method: Dog::playBall) */
PHP_METHOD(dog, playBall)
{
php_printf("%s is playing with a ball!", Z_STRVAL_P(GET_PARAM("name")));
}
/* }}} */
/* {{{ Method: bool Dog::getColour() */
PHP_METHOD(dog, getColour)
{
// yeah, the stupid zend engine programmers made a function
// that can take 0, 1 or 2 arguments, but if you want 3 or 4 args?
// then you have to use this enormous chunk of boilerplate code
// see: https://github.com/twigphp/Twig/blob/1.x/ext/twig/twig.c
// search for: TWIG_CALL_USER_FUNC_ARRAY
// You probably should wrap up this ugly shit in a function call
// But I've copied it directly here because it's easier
zend_call_method(
&this_ptr, Z_OBJCE_P(this_ptr), NULL,
// CAREFUL! php methods are in lower case!!
"playball", strlen("playball"),
// this is zval *retval or NULL if you dont want a return value
NULL,
// means zero parameters
0,
// arg 1
NULL,
// arg 2
NULL
TSRMLS_CC
);
RETURN(GET_PARAM("colour"));
}
/* }}} */
ZEND_BEGIN_ARG_INFO_EX(arginfo_construct, 0, 0, 1)
ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, colour)
ZEND_END_ARG_INFO()
const zend_function_entry dog_functions[] = {
// public methods
PHP_ME(dog, __construct, arginfo_construct, ZEND_ACC_PUBLIC)
PHP_ME(dog, getColour, NULL, ZEND_ACC_PUBLIC)
// protected methods
PHP_ME(dog, playBall, arginfo_createmanager, ZEND_ACC_PROTECTED)
PHP_FE_END
};
/* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(MyAnimals)
{
zend_class_entry entry;
// Creates a class like this \MyAnimals\Dog
INIT_NS_CLASS_ENTRY(entry, "MyAnimals", "Dog", dog_functions);
dog_ce = zend_register_internal_class(&entry TSRMLS_CC);
// Declare the state / error properties
DECLARE_STRING("name", "", ZEND_ACC_PUBLIC);
DECLARE_LONG("age", 0, ZEND_ACC_PRIVATE);
DECLARE_STRING("colour", "", ZEND_ACC_PROTECTED);
return SUCCESS;
}
/* }}} */

Related

How to make a list with just the used inputs for a C module

I have a large module that uses a very large input buffer, consisting of many structures which, in turn, contain other structures and in the end each structure has several variables.
Out of these hundreds of input variables, my module (standalone C entity) uses only a fraction.
I would like to know if there is a way to make a list that will contain only the variables used in my module (would be perfect if it contains the variable type and links to structure/s that contains it).
I tried Doxygen (1.8.5) but I could generate a doc with all input variables, only.
[Later EDIT]
I add an example code and the desired outcome:
#include <stdio.h>
typedef struct subS1{
unsigned char bIn1;
unsigned char bIn2;
} subS1;
typedef struct S1{
struct subS1 stMySubStruct1;
struct subS1 stMySubStruct2;
struct subS1 stMySubStruct3;
} MyInputStruct_t;
void Foo1(MyInputStruct_t *Input);
void Foo2(MyInputStruct_t *Input);
MyInputStruct_t stMyInputStruct = {{1, 2}, {0, 0}, {9, 6}}; // large input buffer
int main() {
Foo1(&stMyInputStruct); // call to my Module 'main' function
return 0;
}
void Foo1(MyInputStruct_t *Input)
{
if(Input->stMySubStruct1.bIn1 == 1)
{
printf("bIn1 = %d\n", Input->stMySubStruct1.bIn1); // stMySubStruct1.bIn1 is used (read or write)
}
Foo2(Input);
return;
}
void Foo2(MyInputStruct_t *Input)
{
if(Input->stMySubStruct3.bIn2 == 0)
{
printf("bIn2 = %d\n", Input->stMySubStruct3.bIn2); // stMySubStruct3.bIn2 is used (read or write)
}
return;
}
The list with just the used inputs for Foo1(): e.g
stMyInputStruct.stMySubStruct1.bIn1 -> is used in Foo1()
stMyInputStruct.stMySubStruct1.bIn2 -> is NOT used
...
stMyInputStruct.stMySubStruct3.bIn2 -> is used in Foo2()
This is just a five-minute hack to demonstrate what I mean, so take it with a grain of salt and for what it is.
So first I downloaded pycparser from https://github.com/eliben/pycparser/
Then I edit the C-generator from https://github.com/eliben/pycparser/blob/master/pycparser/c_generator.py
... adding two lines to the constructor-code (adding two vars struct_refs + struct_ref):
class CGenerator(object):
""" Uses the same visitor pattern as c_ast.NodeVisitor, but modified to
return a value from each visit method, using string accumulation in
generic_visit.
"""
def __init__(self, reduce_parentheses=False):
""" Constructs C-code generator
reduce_parentheses:
if True, eliminates needless parentheses on binary operators
"""
# Statements start with indentation of self.indent_level spaces, using
# the _make_indent method.
self.indent_level = 0
self.reduce_parentheses = reduce_parentheses
# newly added variables here
self.struct_refs = set()
self.struct_ref = None
Then I edit two visitor-functions, to make them save information about the struct-references they parse:
def visit_ID(self, n):
if self.struct_ref:
self.struct_refs.add(self.struct_ref + "->" + n.name)
return n.name
def visit_StructRef(self, n):
sref = self._parenthesize_unless_simple(n.name)
self.struct_ref = sref
self.struct_refs.add(sref)
res = sref + n.type + self.visit(n.field)
self.struct_ref = None
return res
Running this modified piece of Python script over your example code, collects this information:
>>> cgen.struct_refs
{'Input',
'Input->stMySubStruct1',
'Input->stMySubStruct1->bIn1',
'Input->stMySubStruct3',
'Input->stMySubStruct3->bIn2'}
So with a bit more work, it should be able to do the job more generally.
This of course breaks apart in the face of memcpy, struct-member-access-through-pointers etc.
You can also try exploiting structure in your code as well. E.g. If you always call your input-struct "Input", things gets easier.

Best way to extract all enums from C project

I'm writing my own enums-based backtrace. Basically each function call will append it's error to a pre-defined stack which will eventually include all errors across all function calls.
I want to create an efficient way to parse it. In the end I got int backtrace[] with enum values from all over my codebase, and I'd like to relate each enum value to it's name.
This should of course be done post-failure (not at runtime), so what I'm missing is a way, maybe as a part of the compilation step, to dump all my enums and there values to a file. Then i can write a script that parse the errors.
Is there an easy (cross-platform) static tool that does that?
Not sure how you log the errors, but how about:
typedef enum
{
E_SUCCESS = 0,
E_FAIL,
...
} error_t;
typedef struct
{
error_t code;
char * error_name;
} backtrace_entry_t;
backtrace_entry_t backtrace[..];
#define FUNC_EXIT(error_code__) \
do { \
backtrace[func_id].code = (error_code__); \
backtrace[func_id].error_name = #error_code__; \
} while (0)
Then, when you call FUNC_EXIT(E_SUCCESS);, you'll get for the backtrace for the function to be: {.code = 0, .error_name = "E_SUCCESS"}
The problem is, you can't get the right name if you call FUNC_EXIT(var);, where var is some local variable.
Another option, although still not an automagical one:
typedef enum
{
E_SUCCESS = 0,
E_FAIL,
...
NOF_ERROR_CODES
} error_t;
#define MAP_E_CODE_TO_NAME(error_code__) [error_code__] = #error_code__
const char * error_to_name_mapping[NOF_ERROR_CODES] = {
MAP_E_CODE_TO_NAME(E_SUCCESS),
MAP_E_CODE_TO_NAME(E_FAIL),
...
};
Will give a const char * array with {"E_SUCCESS", "E_FAIL", ...}, which you can use like this:
printf("The name of error code %d is '%s'", error, error_to_name_mapping[error]);
The problem here is that you have to have positive value to errors with increasing values.

Why can a function be called not using its exact name?

I found such following function declared in a header file "gobjects.h":
(It is from Stanford Portable Library <gobjects.h>. The source code and header files of the whole library can be found here:
https://github.com/cs50/spl/tree/master/c)
/*
* Function: getWidth
* Usage: width = getWidth(gobj);
* ------------------------------
* Returns the width of this object, which is defined to be the width of
* the bounding box.
*/
double getWidthGObject(GObject gobj);
It confuses me that the name of the function is getWidthGObject, but in comment block it specifies the usage as if the name is getWidth. And when I call this function in my own code, it seems both names work fine. Just to clarify there is not another function named getWidth declared in this header file.
So, my question is, why can we call this function in two different names, while the shorter one of them seems never defined?
getWidth is in generic.h:
#define getWidth(arg) getWidthGeneric(sizeof arg, arg)
Comments are often out of date, but it doesn't look like that's the issue here. getWidthGeneric ends up calling getWidthGObject in generic.c. arg is constructed with the ... va_list:
double getWidthGeneric(int size, ...) {
...
type = getBlockType(arg);
if (endsWith(type, "GWindow")) {
return getWidthGWindow((GWindow) arg);
} else if (endsWith(type, "GObject")) {
return getWidthGObject((GObject) arg);
} else {
error("getWidth: Illegal argument type");
}
}

Replacing deprecated "dev_attrs" attribute with "dev_groups"

I'm trying to compile a Linux device driver (kernel module), however the module was last updated in April 2013 and of course it doesn't compile anymore on a recent (3.13) kernel, here's the error :
als_sys.c:99:2: error: unknown field ‘dev_attrs’ specified in initializer
I've searched already but all that I found were patches, there is no clear "tutorial" about updating an old module, the only thing I understood was that I need to use dev_groups instead, but it doesn't accept the same value as dev_attrs and I don't know how to adapt the existing code for that.
The code (some of it, the entire code can be found here) :
# als_sys.c
static ssize_t
illuminance_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct als_device *als = to_als_device(dev);
int illuminance;
int result;
result = als->ops->get_illuminance(als, &illuminance);
if (result)
return result;
if (!illuminance)
return sprintf(buf, "0\n");
else if (illuminance == -1)
return sprintf(buf, "-1\n");
else if (illuminance < -1)
return -ERANGE;
else
return sprintf(buf, "%d\n", illuminance);
}
# truncated - also "adjustment_show" is similar to this function so
# I didn't copy/paste it to save some space in the question
static struct device_attribute als_attrs[] = { # that's what I need to modify, but
__ATTR(illuminance, 0444, illuminance_show, NULL), # I have no clue what to
__ATTR(display_adjustment, 0444, adjustment_show, NULL), # put here instead
__ATTR_NULL,
};
# truncated
static struct class als_class = {
.name = "als",
.dev_release = als_release,
.dev_attrs = als_attrs, # line 99, that's where it fails
};
EDIT
As mentioned in the answer below, I changed the code like this :
static struct device_attribute als_attrs[] = {
__ATTR(illuminance, 0444, illuminance_show, NULL),
__ATTR(display_adjustment, 0444, adjustment_show, NULL),
__ATTR_NULL,
};
static const struct attribute_group als_attr_group = {
.attrs = als_attrs,
};
static struct class als_class = {
.name = "als",
.dev_release = als_release,
.dev_groups = als_attr_group, # line 103 - it fails here again
};
But I still get another error :
als_sys.c:103:2: error: initializer element is not constant
I've found this question which is about the same error however its answer is about a single attribute and I don't know how to adapt it for multiple ones.
Thanks for your help and have a nice day.
Indeed, dev_attrs was replaced with dev_groups in 3.13. In 3.12 they were both presented in struct. Look at 3.12 version and 3.13 version.
Anyway, there should not be a problem, because simple search for attribute_group gives you a lot of examples.
Simply put, you have to embed your dev_attrs inside dev_group:
static const struct attribute_group als_attr_group = {
.attrs = als_attrs,
};
And then use that attribute group in struct class.
There is also a handy macro ATTRIBUTE_GROUPS. See example usage https://lkml.org/lkml/2013/10/23/218.
EDIT:
Remove const declaration from attribute group like this:
static struct attribute_group als_attr_group = {
.attrs = als_attrs,
};
Because you can't initialize const struct with something that is not literal like 0xff or 'c'. See more detailes here.

redefinition of *_get_type(void) gtk+ requried method

As already said by the headline, I get a compile error I seem to be unable to fix:
error: redefinition of 'tinygecko_notebook_get_type'
note: previous definition of 'tinygecko_notebook_get_type' was here
Where error points to this line (the first of this codes snippet):
GType
tinygecko_notebook_get_type (void)
{
static GType type = 0;
if (type == 0) {
static const GTypeInfo info = {
sizeof (TinygeckoNotebookClass), /* size of class struct */
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc)tinygecko_notebook_class_init, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (TinygeckoNotebook),
0, /* n_preallocs */
(GInstanceInitFunc)tinygecko_notebook_init /* instance_init */
};
type = g_type_register_static (GTK_TYPE_NOTEBOOK, "TinygeckoNotebook", &info, 0);
}
return type;
}
and the note line points to the type setup
G_DEFINE_TYPE (TinygeckoNotebook, tinygecko_notebook, GTK_TYPE_NOTEBOOK);
Both snippets are located within the .c file (the note line is above the error line).
Help appreciated.. I am confused. Why should that gtk+ macro redefine a function which I have to setup for own gobject based class initalizer and finalizer (if they exist) (in this case based on GtkNotebook).
G_DEFINE_TYPE is a shortcut to allow you to avoid writing the get_type function. So you don't want to use G_DEFINE_TYPE if you're implementing the get_type function by hand.
In this case I don't notice anything special in your handcoded implementation, looks like just the usual boilerplate, so you can probably just delete it and use G_DEFINE_TYPE.
There are also variants of G_DEFINE_TYPE such as G_DEFINE_TYPE_WITH_CODE, G_DEFINE_ABSTRACT_TYPE, G_DEFINE_TYPE_EXTENDED, etc. that let you deviate from pure boilerplate a bit and still avoid doing it all by hand.

Resources