3

To enable better collaboration between some UI guy who know HTML and the backend guy who know .NET we're thinking of an architecture where we use an MVC web app and use phalanger as the view engine.

Integrating phalanager as a view model looks fairly easy apart from one point. I'm not sure how to pass the model to the page script. Any ideas how this might be achieved?

One idea would be to calling a static .NET method from the php script, but it feels a bit hacky. I'd like be able to just pass the parameter to the script and have the script be able to pick it up in a variable.

tereško
  • 58,060
  • 25
  • 98
  • 150
Robert
  • 6,407
  • 2
  • 34
  • 41

2 Answers2

4

Using .NET on the backend and PHP (using Phalanger) on the frontend looks like a good scenario. I think that the best option is to implement the model in .NET and use PHP to implement controllers and views (either directly or using some PHP framework).

Calling a .NET model from PHP compiled using Phalanger is quite easy, because the PHP code can access .NET objects. Assuming you have a C# DLL containing a namespace DemoDataLayer with the following type:

public class Data {
  public List<Category> GetCategories() {
    var ret = new List<Category>();
    // Some code to load the data
    return ret;
  }
}

Then you can reference the C# library from the Phalanger website (using web.config) and use PHP extensions provided by Phalanger to use the Data class as if it was a standard PHP object:

<?
  import namespace DemoDataLayer;
  $dl = new Data;
  $categories = $dl->GetCategories();
?>
<ul>
<? foreach($categories as $c) { ?>
    <li><a href="products.php?id=<? echo $c->ID ?>"><? echo $c->Name ?></a></li>
<? } ?>
</ul>

To configure the reference, you'll need to add the C# DLL to the bin directory and include it in classLibrary element. The import namespace syntax used above is a Phalanger-specific language extension (for using .NET namespaces) that needs to be turned on using PhpClr feature:

<?xml version="1.0"?>
<configuration>
  <phpNet>
    <compiler>
      <set name="LanguageFeatures">
        <add value="PhpClr" />
      </set>
    </compiler>
    <classLibrary>
      <add assembly="DemoDataLayer" />
    </classLibrary>
  </phpNet>
</configuration>
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • Thanks for the answer Tomas, but I was hoping to do it the other way round: have the .NET code call the php script and pass it parameter to automatically create a binding like "$categories" to minimize the amount of "php .net" code that had to be written. Is this possible? – Robert May 27 '11 at 10:23
  • @Robert: Yes, there is some way to do that too (I'll need to check for the details). The question is, how do you want to instantiate the PHP code? If you're using ASP.NET MVC, I assume you'd like to have something like `return PhpView("foo.php")` in your controller? (There is no support for this currently, but it sounds interesting and it could surely be done!) – Tomas Petricek May 27 '11 at 23:14
  • I have my own mvc like framework which has it's own F#ish way of invoking views, but basically you'd do something like return PhpView("foo.php") to invoke the php view. I've been looking at it right now, and it looks difficult as all the stuff relating to compilation of scripts is marked as internal. Looks like it'd need to make a custom build of phalanger and change some visibility settings. – Robert May 28 '11 at 07:05
1

Check out http://phpviewengine.codeplex.com/

That project contains the following method for converting CLR types to a form that your PHP scripts can use:

    object PhpSafeType(object o)
    {
        // PHP can handle bool, int, double, and long
        if ((o is int) || (o is double) || (o is long) || (o is bool))
        {
            return o;
        }
        // but PHP cannot handle float - convert them to double
        else if (o is float)
        {
            return (double) (float) o;
        }
        // Strings and byte arrays require special handling
        else if (o is string)
        {
            return new PhpString((string) o);
        }
        else if (o is byte[])
        {
            return new PhpBytes((byte[]) o);
        }
        // Convert .NET collections into PHP arrays
        else if (o is ICollection)
        {
            var ca = new PhpArray();
            if (o is IDictionary)
            {
                var dict = o as IDictionary;
                foreach(var key in dict.Keys)
                {
                    var val = PhpSafeType(dict[key]);
                    ca.SetArrayItem(PhpSafeType(key), val);
                }
            }
            else
            {
                foreach(var item in (ICollection) o)
                {
                    ca.Add(PhpSafeType(item));
                }
            }
            return ca;
        }
        // PHP types are obviously ok and can just move along
        if (o is DObject)
        {
            return o;
        }
        // Wrap all remaining CLR types so that PHP can handle tham
        return PHP.Core.Reflection.ClrObject.WrapRealObject(o);
    }

It can be used like this...

// Get setup
var sc = new ScriptContext.CurrentContext;
var clrObject = /* Some CLR object */
string code = /* PHP code that you want to execute */

// Pass your CLR object(s) into the PHP context
Operators.SetVariable(sc,null,"desiredPhpVariableName",PhpSafeType(clrObject));

// Execute your PHP (the PHP code will be able to see the CLR object)
var result =            return DynamicCode.Eval(
                code,
                false,
                sc,
                null,
                null,
                null,
                "default",
                1,1,
                -1,
                null
                );

This can even handle anonymous types. For example, insert the following into the above.

var clrObject = new { Name = "Fred Smith" };
Operators.SetVariable(sc,null,"person",PhpSafeType(clrObject));

You could then access this in the PHP:

echo $person->Name;

which of course outputs Fred Smith

Justin
  • 8,853
  • 4
  • 42
  • 42