MrCompiler
Operator overloading simulation for PHP.
PHP doesn’t support operator overloading. Cs2php is capable of simulating this feature while converting C# code to PHP.
Assume simple C# class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Lang.Php.Examples.BasicFeaturesExample { [Module("class.complex-number")] public class ComplexNumber { #region Constructors public ComplexNumber(double real, double imaginary) { this.Real = real; this.Imaginary = imaginary; } #endregion Constructors #region Static Methods // Public Methods [ScriptName("minus")] public static ComplexNumber operator -(ComplexNumber a, ComplexNumber b) { return new ComplexNumber(a.Real - b.Real, a.Imaginary - b.Imaginary); } [ScriptName("plus")] public static ComplexNumber operator +(ComplexNumber a, ComplexNumber b) { return new ComplexNumber(a.Real + b.Real, a.Imaginary + b.Imaginary); } public static implicit operator ComplexNumber(double x) { return new ComplexNumber(x, 0); } #endregion Static Methods #region Properties public double Imaginary { get; set; } public double Real { get; set; } #endregion Properties public static ComplexNumber operator *(ComplexNumber a, ComplexNumber b) { return new ComplexNumber(a.Real * b.Real - a.Imaginary * b.Imaginary, a.Real * b.Imaginary + a.Imaginary * b.Real); } public static ComplexNumber operator /(ComplexNumber a, ComplexNumber b) { var tmp = b.Real * b.Real + b.Imaginary * b.Imaginary; var re = a.Real * b.Real + a.Imaginary * b.Imaginary; var im = a.Imaginary * b.Real - a.Real * b.Imaginary; return new ComplexNumber(re / tmp, im / tmp); } } } |
Class represents complex number. Some operators like multiplication has been defined in it. Following code represents ComplexNumber
on PHP side.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php class ComplexNumber { public Imaginary; public Real; public function __construct($real, $imaginary) { $this->Real = $real; $this->Imaginary = $imaginary; } public static function minus($a, $b) { return new self($a->Real - $b->Real, $a->Imaginary - $b->Imaginary); } public static function plus($a, $b) { return new self($a->Real + $b->Real, $a->Imaginary + $b->Imaginary); } public static function op_Implicit($x) { return new self($x, 0); } public static function op_Multiply($a, $b) { return new self($a->Real * $b->Real - $a->Imaginary * $b->Imaginary, $a->Real * $b->Imaginary + $a->Imaginary * $b->Real); } public static function op_Division($a, $b) { $tmp = $b->Real * $b->Real + $b->Imaginary * $b->Imaginary; $re = $a->Real * $b->Real + $a->Imaginary * $b->Imaginary; $im = $a->Imaginary * $b->Real - $a->Real * $b->Imaginary; return new self($re / $tmp, $im / $tmp); } } ?> |
Operator methods has been converted exactly like normal static methods. Method name was taken from C# (like op_Multiply
) or from ScriptNameAttribute
(like minus method).
Let’s take a look at operators usage.
1 2 3 4 |
var a = new ComplexNumber(3, 2); ComplexNumber b = 12; var c = a * b; var d = a + b; |
And the same code in PHP
1 2 3 4 |
$a = new ComplexNumber(3, 2); $b = ComplexNumber::op_Implicit(12); $c = ComplexNumber::op_Multiply($a, $b); $d = ComplexNumber::plus($a, $b); |
As you can see implicit conversion of a real number to a complex number was converted to a static method ComplexNumber::op_Implicit
call. Moreover the addition and multiplication have been converted in similar way.
Of course CS2PHP can convert more complicated code, like this:
1 |
var e = (a + 17) * 3; |
1 |
$e = ComplexNumber::op_Multiply(ComplexNumber::plus($a, ComplexNumber::op_Implicit(17)), ComplexNumber::op_Implicit(3)); |
We can define more than one implementation for each operator. For example we can define two independent multiplication operators:
1 2 3 4 5 6 7 8 9 10 |
[ScriptName("mul1")] public static ComplexNumber operator *(ComplexNumber a, ComplexNumber b) { return new ComplexNumber(a.Real * b.Real - a.Imaginary * b.Imaginary, a.Real * b.Imaginary + a.Imaginary * b.Real); } [ScriptName("mul2")] public static ComplexNumber operator *(ComplexNumber a, double b) { return new ComplexNumber(a.Real * b, a.Imaginary * b); } |
We have to decorate at least one definition with ScriptNameAttribute
in order to specify different PHP names for both methods.
1 2 3 4 5 6 |
public static function mul1($a, $b) { return new self($a->Real * $b->Real - $a->Imaginary * $b->Imaginary, $a->Real * $b->Imaginary + $a->Imaginary * $b->Real); } public static function mul2($a, $b) { return new self($a->Real * $b, $a->Imaginary * $b); } |
And now we can test this solution using following code:
1 2 |
var m1 = new ComplexNumber(3, 2) * new ComplexNumber(4, 5); var m2 = new ComplexNumber(3, 2) * 5; |
1 2 |
$m1 = ComplexNumber::mul1(new ComplexNumber(3, 2), new ComplexNumber(4, 5)); $m2 = ComplexNumber::mul2(new ComplexNumber(3, 2), 5); |
How to create dynamic images with C#?
Overview
Lang.Php.Graph namespace contains Image class that represents PHP resource related to GD image that can be obtained using functions like imagecreatetruecolor
. Image class is also container for image related methods.
On PHP side we have a number of functions with name starting from image
prefix accepting GD image resource as first argument like this.
1 |
int imagecolorallocate ( resource $image , int $red , int $green , int $blue ) |
On C# side we can use instance methods usually with shorter names and with reduced argument list. For example imagecreatetruecolor
equivalent is defined as:
1 2 |
[DirectCall("imagecolorallocate", "this,0,1,2")] public Color ColorAllocate(int red, int green, int blue) |
And we can use this method like this:
1 |
var color = myImage.ColorAllocate(0, 127, 255); |
Color, Font and other
Lets take a look at imagestring
function defined as
1 |
bool imagestring ( resource $image , int $font , int $x , int $y , string $string , int $color ) |
This function accepts font and color as integer value, so it is easy to make mistake by putting any integer value that was not intended to be font or color.
To avoid this ambiguity Lang.Php.Graph
namespace contains classes like Color
or Font
. By using this classes we can produce more reliable code even if on PHP side they are nothing more than integers. That’s why DrawString (imagestring equivalent) is defined as
1 2 |
[DirectCall("imagestring", "this,0,1,2,3,4")] public bool DrawString(Font font, int x, int y, string text, Color color) |
How can we use Color
class?
ColorAllocate
method returns Color
while its PHP prototype returns int
(if success) or FALSE
when action fails.
Meticulous approach to programming requires error checking. Color
class contains static function IsValid
, defined as
1 2 |
[UseBinaryExpression("!==", "false", "$0")] public static bool IsValid(Color image) |
And its typical usage is
1 2 3 |
color = myImage.ColorAllocate(i, 255 - i, 0); if (Color.IsValid(color)) myImage.DrawLine(0, HEIGHT - 1, i, 0, color); |
which is translated into
1 2 3 |
$color = imagecolorallocate($myImage, $i, 255 - $i, 0); if (false !== $color) imageline($myImage, 0, self::HEIGHT - 1, $i, 0, $color); |
Example
Code below is a part of example available on GitHub. Class DynamicImage
can generate images like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
[Page("image")] class DynamicImage : PhpDummy { #region Static Methods // Public Methods public static void PhpMain() { int compression = FilterInput.ValidateInt(FilterInput.Type.Get, "compression", IntFlags.AllowHex, new IntOptions() { Default = 90 }).Value; if (Script.Get.ContainsKey("format") && Script.Get["format"] == "jpg") CreateImage(true, compression); else CreateImage(false, compression); } // Private Methods private static void CreateImage(bool jpg, int compression) { if (compression < 0) compression = 0; else if (compression > 100) compression = 100; if (jpg) header(Image.HEADER_CONTENT_TYPE_JPG); else header(Image.HEADER_CONTENT_TYPE_PNG); var myImage = Image.CreateTrueColor(WIDTH, HEIGHT); if (Image.IsValid(myImage)) { Color color = myImage.ColorAllocate(255, 0, 0); // first call is background for (int i = 0; i < WIDTH; i++) { color = myImage.ColorAllocate(i, 255 - i, 0); myImage.DrawLine(0, HEIGHT - 1, i, 0, color); color = myImage.ColorAllocate(i, 0, 255 - i); myImage.DrawLine(WIDTH - 1, 0, WIDTH - 1 - i, HEIGHT - 1, color); } color = myImage.ColorAllocate(0, 0, 255); myImage.DrawString(Font.Font4, 10, 30, "Hello", color); color = myImage.ColorAllocate(255, 255, 255); myImage.DrawString(Font.Font4, 10, 50, "C# to PHP is fantastic", color); if (jpg) myImage.Jpeg(null, compression); else myImage.Png(); myImage.Destroy(); } } #endregion Static Methods #region Fields public const int HEIGHT = 100; public const int WIDTH = 256; #endregion Fields } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
class DynamicImage { const HEIGHT = 100; const WIDTH = 256; public static function PhpMain() { global $_GET; $compression = filter_input(INPUT_GET, 'compression', FILTER_VALIDATE_INT, array('options' => array('default' => 90), 'flags' => FILTER_FLAG_ALLOW_HEX)); if (array_key_exists('format', $_GET) && $_GET['format'] == 'jpg') self::CreateImage(true, $compression); else self::CreateImage(false, $compression); } private static function CreateImage($jpg, $compression) { if ($compression < 0) $compression = 0; else if ($compression > 100) $compression = 100; if ($jpg) header('Content-Type: image/jpg'); else header('Content-Type: image/png'); $myImage = imagecreatetruecolor(self::WIDTH, self::HEIGHT); if (false !== $myImage) { $color = imagecolorallocate($myImage, 255, 0, 0); for($i = 0; $i < self::WIDTH; $i++) { $color = imagecolorallocate($myImage, $i, 255 - $i, 0); imageline($myImage, 0, self::HEIGHT - 1, $i, 0, $color); $color = imagecolorallocate($myImage, $i, 0, 255 - $i); imageline($myImage, self::WIDTH - 1, 0, self::WIDTH - 1 - $i, self::HEIGHT - 1, $color); } $color = imagecolorallocate($myImage, 0, 0, 255); imagestring($myImage, 4, 10, 30, 'Hello', $color); $color = imagecolorallocate($myImage, 255, 255, 255); imagestring($myImage, 4, 10, 50, 'C# to PHP is fantastic', $color); if ($jpg) imagejpeg($myImage, null, $compression); else imagepng($myImage); imagedestroy($myImage); } } } |
Using PHP data structures in C#
Php is full of functions that returns various data structures sometimes quite complicated and not easy to remember. One of most weird structures is return value of getimagesize
. It is an array containing values indexed by both integer and string indices.
Assume output of some example code:
1 |
var_dump(getimagesize("path-to-some-image.jpg")); |
We can expect output like this:
array (size=7)
0 => int 3456
1 => int 2592
2 => int 2
3 => string ’width=”3456″ height=”2592″’ (length=26)
'bits’ => int 8
'channels’ => int 3
'mime’ => string ’image/jpeg’ (length=10)
Following PHP manual we know that index 0 and 1 contains respectively the width and the height of the image. Index 2 is value indicating the type of the image and so on.
Let’s start dreaming about more comfortable way to access image properties. Lang.Php.Graph namespace contains class ImageInfo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[AsArray] public class ImageInfo { [ScriptName("0")] public int Width; [ScriptName("1")] public int Height; [ScriptName("2")] public ImageTypes ImageType; [ScriptName("3")] public string HtmlWidthHeight; [ScriptName("bits")] public int Bits; [ScriptName("channels")] public int Channels; [ScriptName("mime")] public string Mime; } |
Note that class is decorated with AsArrayAttribute
so it will not been converted to normal PHP class. Array will be used instead.
Fields are decorated with ScriptNameAttribute
so every C# expression using field of ImageInfo class is converted into PHP array access expression.
For example:
1 2 3 |
var imageInfo = GetImageSize("myImage.jpg"); var width = imageInfo.Width; var mime = imageInfo.Mime; |
Is converted to
1 2 3 |
$imageInfo = getimagesize('myImage.jpg'); $width = $imageInfo[0]; $mime = $imageInfo['mime']; |
What about image type? On PHP side we have integer value. Value 2 (or IMAGETYPE_JPEG constant) indicates Jpeg.
On C# side we have Lang.Php.Graph.ImageTypes
enum. Source:
1 2 |
if (imageInfo.ImageType == ImageTypes.Jpeg) echo("Image type is JPEG"); |
can be translated to:
1 2 |
if ($imageInfo[2] == IMG_JPG) echo 'Image type is JPEG'; |