En este artículo, exploraremos cómo crear una biblioteca en C# que actúe como una extensión de PHP y devuelva objetos complejos. Usaremos un ejemplo simple con una clase Page
y una colección de páginas que filtraremos según un criterio dado.
Objetivos
- Crear una clase
Page
en C#. - Implementar una función que filtre las páginas según un nombre.
- Devolver una lista de objetos
Page
como una cadena JSON a PHP. - Deserializar el JSON en PHP para trabajar con objetos complejos.
Estructura del Proyecto
A continuación, se muestra la estructura del proyecto que implementaremos:
MyCSharpLibrary/
│
├── MyCSharpLibrary.csproj # Archivo de proyecto C#
├── Program.cs # Archivo de entrada (opcional)
├── Page.cs # Clase Page
├── Pages.cs # Clase Pages
└── MyCSharpLibrary.dll # Biblioteca compilada (versión final)
Paso 1: Crear la Clase en C#
Primero, definimos la clase Page
y una clase Pages
que hereda de List<Page>
. Esta última contendrá métodos para gestionar las páginas.
Código C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.Json;
public class Page
{
public string Name { get; set; }
}
public class Pages : List<Page>
{
// Constructor para agregar páginas de ejemplo
public Pages()
{
this.Add(new Page { Name = "Page1" });
this.Add(new Page { Name = "Page2" });
this.Add(new Page { Name = "AnotherPage" });
}
// Método que devuelve páginas filtradas como JSON
[UnmanagedCallersOnly(EntryPoint = "GetPages")]
public static IntPtr GetPages(IntPtr filterPtr)
{
// Convertir el puntero a una cadena
string filter = Marshal.PtrToStringAnsi(filterPtr);
Pages pages = new Pages();
// Filtrar las páginas que contienen el filtro en el nombre
var filteredPages = pages
.Where(page => page.Name.Contains(filter, StringComparison.OrdinalIgnoreCase))
.ToList();
// Serializar la lista de objetos a JSON
string jsonResult = JsonSerializer.Serialize(filteredPages);
// Devolver el resultado como puntero a cadena ANSI
return Marshal.StringToHGlobalAnsi(jsonResult);
}
}
Explicación del Código
- Clase
Page
: Representa una página con una propiedadName
. - Clase
Pages
: Hereda deList<Page>
y agrega un constructor para inicializar con algunas páginas de ejemplo. - Método
GetPages
:- Usa
Marshal.PtrToStringAnsi
para convertir el puntero a una cadena. - Filtra las páginas basándose en el nombre.
- Serializa la lista filtrada a JSON usando
JsonSerializer
. - Devuelve el resultado como puntero a una cadena ANSI con
Marshal.StringToHGlobalAnsi
.
- Usa
Paso 2: Compilar la Biblioteca C
Compila la biblioteca como una DLL para usarla como extensión en PHP:
dotnet publish -c Release -r linux-x64 --self-contained
Versión Compilada
Después de compilar, deberías tener un archivo llamado MyCSharpLibrary.dll
en la carpeta bin/Release/net6.0/linux-x64/publish/
. Este archivo es la biblioteca que usarás en PHP.
Paso 3: Crear la Extensión en C para PHP
Ahora, necesitamos crear una extensión en C que permita llamar a la función GetPages
desde PHP. Aquí está el código para la extensión:
Código C
#include <php.h>
#include <dlfcn.h>
// Definir el tipo de la función que se va a usar
typedef const char* (*GetPagesFunc)(const char* filter);
// Implementación de la función PHP que llama a GetPages
PHP_FUNCTION(get_pages)
{
char *filter;
size_t filter_len;
// Obtener el argumento de PHP
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &filter, &filter_len) == FAILURE) {
RETURN_FALSE;
}
// Cargar la biblioteca compartida
void* handle = dlopen("MyCSharpLibrary.so", RTLD_LAZY);
if (!handle) {
php_error(E_WARNING, "Cannot load library: %s", dlerror());
RETURN_FALSE;
}
// Obtener el símbolo de la función GetPages
GetPagesFunc get_pages = (GetPagesFunc)dlsym(handle, "GetPages");
if (!get_pages) {
php_error(E_WARNING, "Cannot load symbol: %s", dlerror());
dlclose(handle);
RETURN_FALSE;
}
// Llamar a la función GetPages
const char* pages = get_pages(filter);
dlclose(handle);
RETURN_STRING(pages);
}
// Tabla de funciones
const zend_function_entry my_functions[] = {
PHP_FE(get_pages, NULL)
PHP_FE_END
};
// Definición del módulo
zend_module_entry my_module_entry = {
STANDARD_MODULE_HEADER,
"my_extension",
my_functions,
NULL, NULL, NULL, NULL, NULL,
"0.1",
STANDARD_MODULE_PROPERTIES
};
// Inicialización del módulo
ZEND_GET_MODULE(my_extension)
Explicación del Código
get_pages
: Esta función recibe un argumento de filtro, carga la biblioteca compartida y obtiene el puntero a la funciónGetPages
.dlopen
: Carga la biblioteca compartida.dlsym
: Busca la funciónGetPages
en la biblioteca.RETURN_STRING(pages)
: Devuelve la cadena JSON a PHP.
Paso 4: Compilar la Extensión
Compila la extensión usando los siguientes comandos:
phpize
./configure
make
sudo make install
Asegúrate de que el archivo de configuración de PHP (php.ini
) tenga la siguiente línea para habilitar la extensión:
extension=my_extension.so
Paso 5: Usar la Función en PHP
Finalmente, puedes utilizar la función get_pages
en tu script PHP para obtener el objeto complejo.
Código PHP
<?php
// Llamar a la función get_pages con un filtro
$json = get_pages("Page");
// Deserializar el JSON en un array de objetos
$pages = json_decode($json);
// Iterar sobre las páginas y mostrar sus nombres
foreach ($pages as $page) {
echo "Name: " . $page->Name . "\n";
}
?>
Explicación del Código
get_pages("Page")
: Llama a la función que filtra las páginas.json_decode($json)
: Deserializa la cadena JSON a un array de objetos PHP.$page->Name
: Accede a la propiedadName
de cada objetoPage
y la muestra.
Conclusión
En este artículo, hemos aprendido cómo crear una biblioteca en C# que actúa como una extensión de PHP, devolviendo objetos complejos en forma de JSON. Este enfoque permite una integración efectiva entre C# y PHP, lo que te permite aprovechar las capacidades de ambos lenguajes.
Consideraciones Finales
- Asegúrate de tener instaladas las herramientas necesarias para compilar y ejecutar el código.
- Puedes expandir este ejemplo para incluir más propiedades en la clase
Page
o manejar diferentes tipos de filtros. - Siempre maneja adecuadamente los errores, especialmente al cargar bibliotecas y funciones dinámicamente.
Este artículo te proporciona una guía clara y detallada sobre cómo integrar C# y PHP usando una DLL, mostrando cómo devolver objetos complejos y procesar esa información en PHP. ¡Espero que lo encuentres útil!