Just an idea, possibly not a new one, and I promise, it is not another “framework”:
Is it worth to create a class library as a DLL for a business model and use that DLL in PHP?
What are the advantages and disadvantages?
As soon as we say DLL, it is a solution that can be used only in Windows
First, let us discuss what we mean about this class library.
1. For proof of concept, our classes will be very primitive. Here is a sample C# namespace:
using System; namespace myClassLib { public class Class1 { public string s1; public Class1(){ s1 = "default s1"; } } public class Class2 { public string s2; public Class1 c1; public Class2(){ s2 = "default s2"; c1 = new Class1(); } public bool changeData(){ s2 = "who did it?"; c1.s1 = "s1 did it"; return true; } } }
There is nothing fancy about it; only 2 related classes with string properties.
2. Compile the class library and copy the myClassLib.dll into the Apache binary folder, next to the httpd.exe, start Apache.
3. Write a simple PHP code, load the library (DOTNET) and refer to the classes in it:
<?php $obj2 = new DOTNET("myClassLib,Version=1.0.3695.2377,Culture=neutral", "myClassLib.Class2"); $obj1 = $obj2->c1; echo 'obj2->s2 = ' . $obj2->s2 . '<br>'; echo 'obj2->c1->s1 = ' . $obj2->c1->s1 . '<br>'; echo 'obj1->s1 = ' . $obj1->s1 . '<br>'; $obj2->changeData(); echo 'obj2->s2 = ' . $obj2->s2 . '<br>'; echo 'obj2->c1->s1 = ' . $obj2->c1->s1 . '<br>'; echo 'obj1->s1 = ' . $obj1->s1 . '<br>'; $obj1->s1 = "No, I didn't!"; echo 'obj2->s2 = ' . $obj2->s2 . '<br>'; echo 'obj2->c1->s1 = ' . $obj2->c1->s1 . '<br>'; echo 'obj1->s1 = ' . $obj1->s1 . '<br>'; unset($obj2); ?>
The output should be like:
obj2->s2 = default s2
obj2->c1->s1 = default s1
obj1->s1 = default s1
obj2->s2 = who did it?
obj2->c1->s1 = s1 did it?
obj1->s1 = s1 did it?
obj2->s2 = who did it?
obj2->c1->s1 = No, I didn’t!
obj1->s1 = No, I didn’t!
Please note if you came across the problem of loading the assembly, I changed my file called AssemblyInfo.cs and set type visibility to true.
//some code // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. [assembly: ComVisible(true)] //some code
Advantages:
1. You can see the business model or logic is in a compiled and secure state inside a DLL.
2. I think the library is loaded for every request, but I expect it to be faster than loading PHP classes in text files.
3. Instead of writing a PHP extension in C with difficulty, you can do it with one of many programming languages supported by .Net easily.
Disadvantages:
1. You have to re-compile the DLL after each modification.
2. There may be overhead of loading the DLL. There are PHP caching tools as well.
3. Stability: I noticed that Apache has crashed a few times.
4. You may be forced to learn another programming language.
So, it can be done.
Next steps:
1. Testing and sorting out stability issues.
2. Testing with many classes, more complex classes and types, with more interaction with PHP (passing data in and out).
3. Finding a way of loading the DLL only once when Apache (web server process) is started, such as OLE Automation Servers, Word, Excel.
4. Benchmarking.
5. Optionally, moving the database operations from PHP to the DLL
+ an ORM (Object-relational Mapping) functionality could be ideal
6. Porting: We can achieve all using Java and JVM which can run on operating systems other than Windows
+ also there are JPA (Java Persistence API) tools for Java ready to use
PHP can also load Java classes. Here are some examples.
Happy coding!
I always loved the ease of use of arrays in PHP. Simple, dynamic arrays; arrays with integer indexes or string keys; hash tables, maps, collections, resizable variant arrays; you name it.
In C++, you can refer to Standard Template Library (STL) and use vectors for example if you need a regular dynamically sized array, integers used as keys; or maps if you need to use strings as keys.
In C#, namespace Collections contains similar classes to achieve same results; where a C++ vector is a List or ArrayList, and a map is a Hashtable or Dictionary.
PHP has also similar library called Standard PHP Library (SPL).
Java Collections Framework does the same job with lists and maps.
For common problems, common solutions are provided in these libraries.
using System; using System.Collections; using System.Collections.Generic; namespace MyNamespace { class CAssocArray<T> : System.Collections.IEnumerable { public const bool USE_INT_AUTOINDEX = true; public const int INDEX_TYPE_INT = 0; public const int INDEX_TYPE_STRING = 1; public enum INDEX_TYPE {INTEGER, STRING}; private const int nil = -9999999; private int my_max_key;//my_min_key, my_first_key, my_last_key; private Dictionary<int, T> d; //if true, it is like 0-based normal array, sequential numbers will be used as keys private bool isIntAutoIndexed; private INDEX_TYPE index_type; public CAssocArray() { this.init(0, true); } public CAssocArray(CAssocArray<T> a) { this.init(a.index_type, a.isIntAutoIndexed); foreach (int k in a.d.Keys) { //Object o = a.d[k]; T t = (T) o; this.d[k] = t.MemberwiseClone(); this.d[k] = a.d[k]; } } public CAssocArray(INDEX_TYPE index_type, bool isIntAutoIndexed) { this.init(index_type, isIntAutoIndexed); } private void init(INDEX_TYPE index_type, bool isIntAutoIndexed) { my_max_key = nil;//my_first_key = nil; my_last_key = nil; d = new Dictionary<int, T>(); this.index_type = index_type; this.isIntAutoIndexed = isIntAutoIndexed; } public bool isIndexType_Int() { return this.index_type == CAssocArray<T>.INDEX_TYPE.INTEGER;//INDEX_TYPE_INT; } public bool isIndexType_String() { return this.index_type == CAssocArray<T>.INDEX_TYPE.STRING;//INDEX_TYPE_STRING; } public int size()//alias for c++ "vector" class { return d.Count; } public int Length() { return d.Count; } public int Count() { return d.Count; } public void push_back(T value)//alias for c++ "vector" class { this.insert(value); } /** * Insert value, use the next number in the sequence as key */ public void insert(T value) { if (my_max_key == nil) my_max_key = -1; int key = (my_max_key + 1); //string skey = key.ToString(); this.insert(key, value); } public void insert(string strKey, T value) { int key = strKey.GetHashCode(); this.insert(key, value); } public void insert(int key, T value) { d.Add(key, value); if (key > my_max_key) my_max_key = key; } public bool remove(int key) { bool r = false; if (key != nil) { if (d.ContainsKey(key)) { r = d.Remove(key); if (key == my_max_key) my_max_key = this.getMaxKey(); } } return r; } public bool clear() { d.Clear(); my_max_key = nil; return true; } public bool removeFirst() { int key = this.getFirstKey(); return this.remove(key); } public bool removeLast() { int key = this.getLastKey(); return this.remove(key); } public bool pop_back() { return this.removeLast(); } public List<int> getKeyList() { return d.Keys.ToList(); } public List<int> keys() { return d.Keys.ToList(); } public List<T> getValueList() { return d.Values.ToList(); } public List<T> list() { return d.Values.ToList(); } public List<T> values() { return d.Values.ToList(); } public IEnumerator GetEnumerator() { List<T> list = this.d.Values.ToList(); foreach (T item in list) { yield return item; } } private int getLastKey() { List<int> l = this.getKeyList(); if (l.Count == 0) return nil; return l.Last(); } private int getFirstKey() { List<int> l = this.getKeyList(); if (l.Count == 0) return nil; return l.First(); } private int getMaxKey() { List<int> l = this.getKeyList(); if (l.Count == 0) return nil; return l.Max(); } public T first() { int key = this.getFirstKey(); return this.get(key); } public T last() { int key = this.getLastKey(); return this.get(key); } public T this[int key] { get { return this.getI(key); } set { this.setI(key, value); } } public T this[string key] { get { return this.getS(key); } set { this.setS(key, value); } } private T get(int key)//HashCode { T t = default(T); if (d.ContainsKey(key)) t = d[key]; return t; } private T getI(int key) { int Hkey = this.isIntAutoIndexed ? key : key.ToString().GetHashCode(); return this.get(Hkey); } private T getS(string key) { int Hkey = key.ToString().GetHashCode(); return this.get(Hkey); } private void set(int key, T value)//HashCode, value { if (d.ContainsKey(key)) d[key] = value; else this.insert(key, value); } private void setI(int key, T value) { int Hkey = this.isIndexType_Int() ? key : key.ToString().GetHashCode(); this.set(Hkey, value); } private void setS(string key, T value) { int Hkey = key.GetHashCode(); this.set(Hkey, value); } }//end class }//end namespace //Now, how we can use this class: namespace MyNamespace { class Edge { public int target; public int input; public double probability; public Edge(int target, int input, double probability) { this.target = target; this.input = input; this.probability = probability; } } class Edges : CAssocArray<Edge> { public Edge getClassWithMaxProbability() { double max_prob = 0.0; int max_idx = 0; int n = this.size(); Edge e = new Edge(); if (n == 0) { return e; } foreach (int i in this.getKeyList()) { Edge ei = this[i]; if (max_prob < ei.probability) { max_prob = ei.probability; max_idx = ei.target; e = ei; } } return e; } } class Node { public double probability; public Edges edges; public Node(double probability) { this.probability = probability; this.edges = new Edges(); } } class Nodes : CAssocArray<Node> { public Nodes() { //do something } } class Program{ static void Main(string[] args) { Nodes nodes = new Nodes(); Node node0 = new Node(1.0); Node node1 = new Node(0.0); Node node2 = new Node(0.0); Node node3 = new Node(0.0); nodes.insert(node0); nodes[1] = node1; nodes.insert(node2); nodes[3] = node3; node0.edges.insert(new Edge(1, 11, 0.5)); node0.edges.insert(new Edge(2, 12, 0.5)); node1.edges[2] = new Edge(2, 12, 0.25); node1.edges[3] = new Edge(3, 13, 0.75); node2.edges[1] = new Edge(1, 11, 0.75); node2.edges[3] = new Edge(3, 13, 0.25); node3.edges[0] = new Edge(0, 10, 1.0); nodes[0].probability -= 0.25; nodes[1].probability += 0.25; nodes[0].edges[0].probability -= 0.1; nodes[0].edges[1].probability += 0.1; nodes[3].edges[0].probability = 0.5; nodes[3].edges[1] = new Edge(1, 11, 0.5); foreach(int i in nodes.getKeyList()) { Node node = nodes[i]; foreach(int j in node.edges.getKeyList()) { Edge edge = node.edges[j]; //do something } } } } }//end namespace
What would it look like? This is a simple state machine with some nodes and edges linking nodes to each other.
