Showing posts with label PHP. Show all posts
Showing posts with label PHP. Show all posts

Monday, 23 May 2022

MapGuide dev diary: ... and now we begin the great migration

Previously, we made some important breakthroughs on the PHP8 binding for MapGuide. Our sanity PHP tests passed (meaning that the core essentials of the MapGuide API were working), but the main PHP test suite itself had a large amount of failures.


Thanks to dead-easy PHP debugging in VSCode, I was able to step into the PHP test code line-by-line and was able to discover that most of these failures was due to a change in how we're meant to be reading out expected test results from our test fixture SQLite databases. We were reading out test results as SQLite BLOBs which broke test result comparison in PHP8. Switching over to reading out these test results as strings instead fixed up along with updating how we check for exceptions thrown by the MapGuide API fixed up all but one test case.

That one remaining test case failure is due deleting a resource created in a preceding test that doesn't exist. While I could spend more time finding out why, the fact of the matter is that 99.9% of the current test suite is now passing on Windows, and it would be more fruitful now to sort out the revised bundling and configuration story with PHP 8 and try to get a working MapGuide installation with the PHP 8 bundled installation, fixing up whatever code and configurations along the way until we have all of our PHP-based MapGuide applications fully functional, which for reference is the following:

  • The Site Administrator
  • The Feature Source schema report/preview
  • The AJAX viewer with PHP backend
  • Fusion
In the process of getting a new MapGuide installation with bundled PHP 8 setup, I've changed how Apache httpd is set up to talk to PHP. Previously, we actually did something incorrect. We previously bundled the thread-safe build of PHP and used that for both Apache and IIS setups. It turns out we bundled the wrong kind of build for the IIS configuration, as we should have actually been using the NTS (non-thread-safe) build of PHP if setting up a MapGuide Web Tier with IIS and FastCGI.

In the interest of getting things right this time round and not have to bundle 2 copies of PHP (TS and NTS builds), I've decided to go all in on using the NTS build of PHP for both IIS and Apache. This means that setting up IIS will now use the correct binaries for FastCGI, but means that we can no longer use mod_php when setting up Apache httpd as that can only be used with the thread-safe builds of PHP. Instead, for Apache httpd we will now set up FastCGI for use with PHP as well via mod_fcgid.

So I grabbed a compatible copy of mod_fcgid.so from apachelounge and dropped it into my httpd installation, updated httpd.conf to use FastCGI for PHP, flipped display_errors = On in php.ini, spun up both the MapGuide Server and httpd, opened the MapGuide Site Administrator and was greeted with the following.


And I am so glad to see this error message :)

Because that is an error message reported by PHP, so we know PHP itself (via FastCGI) is working.

Also the error is about a missing constants.php file, which if you've been following my previous posts is a file that is no longer necessary as our new PHP 8 bindings defines the constants within the binary extension itself.

This error is one of who-knows-how-many things in our PHP applications that will need fixing up to be PHP 8 compatible.

Time to start a-fixing!

Friday, 22 April 2022

MapGuide dev diary: Another (tangential) breakthrough!

Since I got the basic sanity checking PHP script running without crashing, there was one problem remaining around MgByteReader contents being read incorrectly. This turned out to be another out-of-date SWIG typemap around byte arrays. Once that got fixed we are able to read out the contents of the MgByteReader without content corruption. This is an important thing to test because a lot of results from the MapGuide API (XML results, rendered map images, map plots, etc) come in the form of MgByteReaders. If we can't read content out of these readers without data corruption we have major problems, but that is fortunately not the case (so far!).

With that out of the way, it was onto to the current PHP test suite for the MapGuide API and getting it fixed up and passing.

But before we go there, I wanted to check if our new PHP binding was leaking memory in the most obvious cases. To that effect, I built our PHP binding with reference counting diagnostics enabled and wrote this PHP script.

  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
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<?php

function exceptionTest() {
	/*
	Expected release book-keeping:
		MgException - 1
	 */
	try {
		$rid = new MgResourceIdentifier("iamnotvalid");
	} catch (MgException $ex) {

	}
}

function throwingFunction() {
	$agfRw = new MgAgfReaderWriter();
	$wktRw = new MgWktReaderWriter();

	$rid = new MgResourceIdentifier("iamnotvalid");
}

function exceptionTest2() {
	/*
	Expected release book-keeping:
		MgAgfReaderWriter - 1
		MgWktReaderWriter - 1
		MgException - 1
	 */
	try {
		throwingFunction();
	} catch (MgException $ex) {

	}
}

function exceptionTest3() {
	/*
	Expected release book-keeping:
		MgAgfReaderWriter - 1
		MgWktReaderWriter - 1
		MgException - 2
	 */
	try {
		$rid = new MgResourceIdentifier("iamnotvalid");
	} catch (MgException $ex) {

	}

	try {
		throwingFunction();
	} catch (MgException $ex) {
		//Previous iterations of the binding would leak the previous $ex when
		//reusing the variable in this catch block
		//If the release book-keeping for MgException is 2, then this is no
		//longer the case
	}
}

function geometryXformTest() {
	/*
	Expected release book-keeping:
		MgCoordinateSystemFactory - 1
		MgCoordinateSystem - 2
		MgTransform - 1
		MgWktReaderWriter - 1
		MgPoint - 2
		MgCoordinateXY - 2
	 */
	$csFactory = new MgCoordinateSystemFactory();
	$cs1 = $csFactory->CreateFromCode("LL84");
	$cs2 = $csFactory->CreateFromCode("WGS84.PseudoMercator");
	$xform = $csFactory->GetTransform($cs1, $cs2);
	$wktRw = new MgWktReaderWriter();
	$pt = $wktRw->Read("POINT (1 2)");
	$coord = $pt->GetCoordinate();
	echo "Point (".$coord->GetX().", ".$coord->GetY().")\n";
	$pt = $pt->Transform($xform);
	$coord = $pt->GetCoordinate();
	echo "XPoint (".$coord->GetX().", ".$coord->GetY().")\n";
}

function connectionServiceTest() {
	/*
	Expected release book-keeping:
		MgUserInformation - 1
		MgSiteConnection - 1
		MgProxyResourceService - 1
		MgProxyFeatureService - 1
		MgProxyRenderingService - 1
		MgProxyMappingService - 1
		MgProxyKmlService - 1
		MgProxyDrawingService - 1
		MgProxyTileService - 1
		MgProxyProfilingService - 1
	 */

	$userInfo = new MgUserInformation("Anonymous", "");
	$site = new MgSiteConnection();
	$site->Open($userInfo);

	$service1 = $site->CreateService(MgServiceType::ResourceService);
	$service2 = $site->CreateService(MgServiceType::FeatureService);
	$service3 = $site->CreateService(MgServiceType::RenderingService);
	$service4 = $site->CreateService(MgServiceType::MappingService);
	$service5 = $site->CreateService(MgServiceType::KmlService);
	$service6 = $site->CreateService(MgServiceType::DrawingService);
	$service7 = $site->CreateService(MgServiceType::TileService);
	$service8 = $site->CreateService(MgServiceType::ProfilingService);
}

function functionWithParam($userInfo) {
	$userInfo->SetLocale("en");
}

function parameterPassingTest() {
	// We want to check parameters of Mg* objects passed do not encounter
	// refcounting shenanigans, but if they do, we want to make sure that
	// proper increment/decrement is happening so that the MgUserInformation
	// is released to 0 refcount (and thus deleted) when this function returns

	/*
	Expected release book-keeping:
		MgUserInformation - 1
	 */
	$userInfo = new MgUserInformation();
	functionWithParam($userInfo);
	echo "Locale: " . $userInfo->GetLocale() . "\n";
}

MgInitializeWebTier("C:\\mg-4.0-install\\Web\\www\\webconfig_dev.ini");

echo "=========== BEGIN - exceptionTest() ===========\n";
exceptionTest();
echo "=========== END - exceptionTest() ===========\n";

echo "=========== BEGIN - exceptionTest2() ===========\n";
exceptionTest2();
echo "=========== END - exceptionTest2() ===========\n";

echo "=========== BEGIN - exceptionTest3() ===========\n";
exceptionTest3();
echo "=========== END - exceptionTest3() ===========\n";

echo "=========== BEGIN - connectionServiceTest() ===========\n";
connectionServiceTest();
echo "=========== END - connectionServiceTest() ===========\n";

echo "=========== BEGIN - parameterPassingTest() ===========\n";
parameterPassingTest();
echo "=========== END - parameterPassingTest() ===========\n";

echo "=========== BEGIN - geometryXformTest() ===========\n";
geometryXformTest();
echo "=========== END - geometryXformTest() ===========\n";

?>

The basic tenets behind this test script are:
  • If we new any Mg* class, we expect it to be released (reference count dropped by 1) once the respective variable goes out of scope. We consider the script to not be leaking if every "new" statement has a corresponding release as reported by our refcounting diagnostics.
  • Any non-primitive value returned by any class in the MapGuide API is also released once its respective captured variable goes out of scope
  • If an MgException is thrown and we catch it, it is released once we exit the respective catch block
  • If we have multiple try/catch blocks on MgException and we use the same variable name for the caught exception, we're not leaking on subsequent catch blocks (this used to happen when we were generating the PHP bindings on the older SWIG/PHP version)
  • Finally any MapGuide API object passed as parameters to other functions does not get touched by reference counting. But if it does, we expect that the reference count for that object is the same before and after the function call.
Running this script produces the following output



The key things to notice in the script output is that every variable assignment is being properly released upon exiting the function and anything that we new'd explicitly was released with a 1 -> 0 reference count transition. When an object's reference count drops to 0, it will be de-allocated on the C++ side. The script output is proof that we aren't leaking objects we explicitly new'd up from the PHP script, they are being properly de-allocated. Some objects (like the MgCoordinateSystem) release and do not drop to 0 reference count, but that is okay because I know that the MapGuide API caches MgCoordinateSystem instances for performances reasons, so they aren't leaking instances. They can live beyond the scope of the function call where I initially requested for such objects.

So now knowing that we aren't leaking for the general cases, we can move onto the main PHP test suite.

Running the main PHP test suite it for the first time (in a long time) showed some PHP errors around ambiguous overloaded methods. All of these errors are because PHP 7/8 must've tightened up some of the type checking and passing null as a parameter to a method that has many overloaded signatures may cause confusion. Either that or our heavily modified version of SWIG that we previously used to generate the PHP binding had custom codegen to specifically handle overloaded method resolution.

Regardless, the fix was simple enough. Where passing null would cause ambiguity, we just needed to add the necessary cast (in our case, it was casting to string) to make sure the correct method signature is invoked. Once that fix was applied, the test suite ran to completion ...


... with a metric crapton of test failures :(

Now to figure out why these test cases are failing, which is where we come to the subject of this post.

Anytime in the past, when I need to debug a PHP script, it was a case of sprinkling var_dump and print_r statements everywhere, running it and try to comprehend the now extra verbose output, rinsing and repeating until I found the problem. Basically, printf debugging.

I had heard about a thing called xdebug, but I had no idea how to pair it with an IDE (and what IDE to even use?) to get the nice integrated debugging experience I take for granted with .net, Java or C++. Debugging PHP script felt so primitive because I simply didn't know better and getting proper debugging set up just sounded too hard.

Now, times have changed. We have Visual Studio Code. It has a nice ecosystem of extensions for working with PHP that supports the version of PHP that we are now targeting. If debugging C++ code inside docker containers turned out to be such a piece of cake, then surely the same can be said for using VSCode to debug PHP scripts with breakpoints, variable inspection and the whole works!

So I searched for a VSCode extension that could help me debug PHP, and the first result was this PHP debug adapter. I followed its instructions to the letter.

I went to the xdebug install wizard and pasted by dumped phpinfo details


Once submitted, the wizard figured out what version of PHP I had and gave me the download link for the xdebug extension to download and further instructions on what php.ini changes I needed to make


Then back to the VSCode extension instructions, I also updated php.ini to activate extra xdebug settings

1
2
xdebug.mode = debug
xdebug.start_with_request = yes

Then in VSCode, I opened the PHP script I intend to run and debug, and created a new launch.json file for my active workspace.


The newly installed VSCode PHP debug extension must've kicked in at this point because it created a launch.json with 3 xdebug launch presets ready to go. I modify the "Launch currently open script" preset to add in the extra command-line arguments needed by my PHP script.


Once I saved the launch.json, I open the PHP script again, stuck some breakpoints, switched to the debug tab, making sure to use the "Launch currently open script" preset.


I press the play button to start debugging and lo and behold! We're now breaking and stepping through PHP code and inspecting variables!


Now I can focus on figuring out why there are so many test failures, at a much faster pace than what I would've normally done in the past!

No doubt when MapGuide Open Source 4.0 is finally out the door, I'll have to write up a variation of this post on how to set up VSCode with PHP debugging for your PHP-based MapGuide applications. Because nobody should be sprinking var_dumps and print_r statements to debug PHP code like I have done in the past when this is many orders of magnitude faster and more productive!

Wednesday, 20 April 2022

MapGuide dev diary: An important breakthrough!

Recently I have gotten back into the grind of MapGuide development pushing to clear the final hurdle of having functional PHP bindings for the MapGuide API.

Since I last touched this code, several things have changed.

  • We originally wanted to target PHP 7.x, but this is no longer the latest. PHP 8.1.x is the latest. This release of PHP 8.1 for Windows is also built with the same MSVC compiler we use for MapGuide and FDO (MSVC 2019), which means the stars have aligned again where we can source official windows PHP binaries for bundling.
  • Our last attempt used SWIG 4.0.2 (the most recent stable release). This release unfortunately cannot generate functional bindings for PHP 8, so our hand has been forced and we are now using and assuming current git master of SWIG for generating PHP bindings. The latest git master also no longer generates a [PHP extension .dll + .php proxy script] combination and goes back to generating just a PHP extension .dll, which is great because that how our (now) legacy PHP binding was generated.
Given these environmental changes, we've re-activated the PHP binding work with a semi-clean slate. A lot of hacks and workarounds that were in place because the previous SWIG generated a [PHP extension .dll + .php proxy script] combination have been removed.

The PHP binding work has now advanced to the stage where we can run this basic sanity test script against a binary PHP 8.1.4 distribution on Windows with no errors or crashes.

  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
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<?php

echo "Initializing web tier";
try {
    MgInitializeWebTier("C:\\mg-4.0-install\\Web\\www\\webconfig_dev.ini");
} catch (MgException $initEx) {
    echo "Init failure!";
    die;
} catch (Exception $ex) {
    echo "[php]: Exception: " . $ex->getMessage() . "\n";
    die;
}
echo "[php]: Initialized\n";

// We haven't and shouldn't need to require/include constants.php
// they are now baked into the PHP extension along with the other
// MapGuide API proxy classes
echo "[php]: Testing some constants\n";
echo "  - " . MgMimeType::Agf . "\n";
echo "  - " . MgMimeType::Kml . "\n";
echo "  - " . MgMimeType::Mvt . "\n";
echo "  - " . MgImageFormats::Png . "\n";
echo "  - " . MgImageFormats::Gif . "\n";
echo "  - " . MgLogFileType::Authentication . "\n";
echo "  - " . MgServerInformationProperties::ClientOperationsQueueCount . "\n";

$user = new MgUserInformation("Anonymous", "");
// Basic set/get
$user->SetLocale("en");
echo "[php]: Locale is: " . $user->GetLocale() . "\n";
$conn = new MgSiteConnection();
$conn->Open($user);
// Create a session repository
$site = $conn->GetSite();
$sessionID = $site->CreateSession();
echo "[php]: Created session: $sessionID\n";
$user->SetMgSessionId($sessionID);
// Get an instance of the required services.
$resourceService = $conn->CreateService(MgServiceType::ResourceService);
echo "[php]: Created Resource Service\n";
$mappingService = $conn->CreateService(MgServiceType::MappingService);
echo "[php]: Created Mapping Service\n";
$resId = new MgResourceIdentifier("Library://");
echo "[php]: Enumeratin'\n";
$resources = $resourceService->EnumerateResources($resId, -1, "");
echo $resources->ToString() . "\n";
echo "[php]: Coordinate System\n";
$csFactory = new MgCoordinateSystemFactory();
echo "[php]: CS Catalog\n";
$catalog = $csFactory->GetCatalog();
echo "[php]: Category Dictionary\n";
$catDict = $catalog->GetCategoryDictionary();
echo "[php]: CS Dictionary\n";
$csDict = $catalog->GetCoordinateSystemDictionary();
echo "[php]: Datum Dictionary\n";
$datumDict = $catalog->GetDatumDictionary();
echo "[php]: Coordinate System - LL84\n";
$cs1 = $csFactory->CreateFromCode("LL84");
echo "[php]: Coordinate System - WGS84.PseudoMercator\n";
$cs2 = $csFactory->CreateFromCode("WGS84.PseudoMercator");
echo "[php]: Make xform\n";
$xform = $csFactory->GetTransform($cs1, $cs2);
echo "[php]: WKT Reader\n";
$wktRw = new MgWktReaderWriter();
echo "[php]: WKT Point\n";
$pt = $wktRw->Read("POINT (1 2)");
$coord = $pt->GetCoordinate();
echo "[php]: X: ".$coord->GetX().", Y: ".$coord->GetY()."\n";
$site->DestroySession($sessionID);
echo "[php]: Destroyed session $sessionID\n";
echo "[php]: Test byte reader\n";
$bs = new MgByteSource("abcd1234", 8);
$content = "";
$br = $bs->GetReader();
$buf = "";
while ($br->Read($buf, 2) > 0) {
    echo "Buffer: '$buf'\n";
    $content .= $buf;
}
echo "[php]: Test byte reader2\n";
$bs2 = new MgByteSource("abcd1234", 8);
$content = "";
$br2 = $bs2->GetReader();
$buf = "";
while ($br2->Read($buf, 3) > 0) {
    echo "Buffer: '$buf'\n";
    $content .= $buf;
}
$agfRw = new MgAgfReaderWriter();
echo "[php]: Trigger an exception\n";
try {
    $agfRw->Read(NULL);
} catch (MgException $ex) {
    echo "[php]: MgException caught\n";
    echo "[php]: MgException - Code: ".$ex->GetExceptionCode()."\n";
    echo "[php]: MgException - Message: ".$ex->GetExceptionMessage()."\n";
    echo "[php]: MgException - Details: ".$ex->GetDetails()."\n";
    echo "[php]: MgException - Stack: ".$ex->GetStackTrace()."\n";
    echo "Is standard PHP exception too? " . ($ex instanceof Exception) . "\n";
} catch (Exception $ex) {
    echo "[php]: Exception: " . $ex->getMessage() . "\n";
}
echo "[php]: Trigger another exception\n";
try {
    $r2 = new MgResourceIdentifier("");
} catch (MgException $ex2) {
    echo "[php]: MgException caught\n";
    echo "[php]: MgException - Code: ".$ex2->GetExceptionCode()."\n";
    echo "[php]: MgException - Message: ".$ex2->GetExceptionMessage()."\n";
    echo "[php]: MgException - Details: ".$ex2->GetDetails()."\n";
    echo "[php]: MgException - Stack: ".$ex2->GetStackTrace()."\n";
    echo "Is standard PHP exception too? " . ($ex2 instanceof Exception) . "\n";
} catch (Exception $ex) {
    echo "[php]: Exception: " . $ex->getMessage() . "\n";
}

echo "Press any key to exit\n";
readline();

?>

This script is a "sanity check" in that it is testing the key aspects of the MapGuide API that *must* function properly.
  • We can init the web tier
  • We can create a site connection
  • We can request instances of MgService-derived subclasses with proper downcasting (eg. if we ask for a resource service, we get a MgResourceService and not its parent MgService)
  • We can catch MgExceptions and inspect all relevant properties from it.
  • We can access constants. Notice we aren't require/include-ing constants.php? That's because we've found a way to get SWIG to bake all MapGuide constants into the PHP extension itself so this file is no longer necessary!
  • We can interact with MgByteReader contents properly
Currently the sanity check script is failing on the last point and I strongly suspect it is another case of SWIG typemaps that were relevant for PHP7 but no longer valid for PHP8. We also need to make sure that this script doesn't leak memory. Also I've yet to get this binding building on Linux nor have I ran this sanity check script on Linux.

Once this is taken care of, the long journey begins to make sure everything else is working in order of importance:
  1. Our PHP test suite passes
  2. Our PHP web tier applications (site administrator, schema report, AJAX viewer) are functional
  3. Fusion is functional
  4. We can build updated Apache and PHP end-to-end on Linux
  5. All of the above is functional in both IIS and Apache web server configurations on both Windows and Linux
Then, and only then we can start thinking about a new MapGuide Open Source 4.0 preview/beta release.

Wednesday, 11 July 2018

Introducing: New experimental bindings for the MapGuide API

I haven't been quiet on the MapGuide front. I've just been deeply entrenched in my lab conducting an important experiment whose results are finally coming into fruition that I am back here to make this exciting announcement.

That experiment was: Can we generate bindings for the MapGuide API using a vanilla version of SWIG? Yes we can!

Background

This is an important question that we needed an answer for and we wanted that answer to be "Yes". 

We currently provide bindings for the MapGuide API in:
  • Java
  • PHP 5.x
  • .net (full framework)
However, we currently use an ancient and heavily modified version of SWIG, whose unclear audit trail of modifications and changes means that being able to support newer versions of PHP (ie. PHP 7.x) or variants of .net like .net Core is nigh-impossible, which puts us in a bit of a pickle because:
  • PHP 5.6 (our current bundled version of PHP) will be end-of-life on December 2018. Bundling and targeting an EOL version of PHP is not a good look. To bundle the current version of PHP (7.x) we need to be able to generate a compatible PHP extension for it. We can't do that with current internal copy of SWIG as the zend extension APIs have massively breaking changes from PHP5 to PHP7.
  • .net Core is where the action is at in the .net space, and not having a presence in this space diminishes our story of being able to build MapGuide applications in .net because as time goes on, that definition of ".net" will assume to mean both the (windows-only) full framework and (cross-platform) .net core.
  • We may want to add support for additional programming languages in the future. Can't do it with our super-modified copy of SWIG again because of the unclear history of changes made to this tool.
Given these factors, and the untenable situation we currently find ourselves in technology-wise, we needed to explore the possibility of generating the bindings using a vanilla (un-modified) version of SWIG. If we're going to go vanilla, we want the latest and greatest, which supports generating bindings for PHP7, and can support .net core with the right amount of SWIG typemap elbow-grease, so all the more motivation to get this working!

What we now have

2 months since the decision to embark on this journey, the mission to get functional MapGuide bindings using vanilla SWIG has been a success! We now have the following bindings for the MapGuide API:
  • A Java binding modeled on the non-crufty official Java binding. Requires Java 7 or higher.
  • A (currently windows-only) PHP extension for PHP 7.1
  • A netstandard2.0-compatible binding for .net that works on both .net Core and full framework and is also cross-platform for platforms where both .net Core and official MapGuide binary packages are available for. For Linux, that means this .net binding works in Ubuntu 14.04 64-bit (where .net Core also has packages available). The nuget package for this binding is fully self-contained and includes the necessary native dependencies (both 32 and 64-bit) needed for the .net library to work. For .net full framework, it includes an MSBuild .targets file to ensure all the required native dependencies are copied out to your application's output directory.
Where to get it

You can grab the bits from the releases page of the mapguide-api-bindings GitHub repo.

For .net, you will have to setup a local package source and drop the nuget package there in order to consume in your library/application.

You will need MapGuide Open Source 3.1.1 installed as this is the only version of MapGuide I am generating these bindings for and testing against. Please observe the current supported platform matrix to see what combinations of technology stacks work with this new set of bindings. Please also observe the respective notes on the .net, PHP and Java bindings to observe what changes and adjustments you need to make in your MapGuide application should you want to try out these bindings.

Sample applications (to make sure this stuff works)

As proof that these bindings work, here's a sample asp.net core application using the new MapGuide .net binding. As a testament to what targeting .net Core gives us, you could bypass building the sample application from source and perhaps give the self-contained package a go. Thanks to the powerful publishing capabilities provided by the dotnet CLI, we can publish a self-contained .net core application with zero external dependencies. In the case of this sample application, you can download the zip, extract it to a windows or Ubuntu 14.04 64-bit machine with a standard MapGuide Open Source 3.1.1 install, run the MvcCoreSample executable within, go to http://localhost:5000 and your MapGuide application is up and running!




For Java and PHP, I'm still cooking up some sample applications in the same vein as the asp.net core one (ie. Porting across the MapGuide Developer's Guide samples), but for now the only verification that these bindings work is that our current binding test suite run to completion (with some failures, but these failures are known failures that are also present in our current bindings).

Where to from here?

I intend for the mapguide-api-bindings project to serve as an incubation area where we can iron out any show-stopping problems before planning for the eventual inclusion into MapGuide proper and supplementing (and in the case of PHP, replacing) our current bindings because eventually, we have to. We cannot keep bundling and targeting PHP 5.x forever. We need to be able to target newer versions of these programming languages, and maybe in some cases new programming languages.

mapguide-api-bindings project repo
asp.net core sample application repo

Saturday, 6 August 2016

MapGuide tidbits: mapguide-rest and PHP 5.6

Since MapGuide Open Source 3.1 Beta 2 shipped with PHP 5.6, some might be wondering if my mapguide-rest extension will work on this version of PHP (from a compatibility standpoint, we mainly are referring to the MapGuide API itself, which is the same as before, and not the surrounding Web Tier environment like PHP, Tomcat, etc).

The good news is: Yes, it works for the most part

I say that because, if you see a message like this:

Automatically populating $HTTP_RAW_POST_DATA is deprecated and will be removed in a future version

It's because of some some deprecation warnings that are active in PHP 5.6. This message in particular, is due to the setting always_populate_raw_post_data not being disabled in php.ini.

And for another interesting fact? It's not set to 1, which should imply the setting is disabled right? Well you guessed wrong, it has to be explicitly set to -1 in php.ini for it to be disabled. Once disabled, these warning messages go away.

Another thing to look out for from the client-side is that PHP 5.6 may include charset in its Content-Type response headers. So if your client code was previously testing for application/json
exactly for a JSON response, that may fail now because PHP 5.6 will most likely send down application/json; charset=utf-8 instead. If you have such code, adjust accordingly.

But besides that, my mapguide-rest function test suite says things are A-OK with this release of PHP.

For a better out-of-the-box experience, we'll look to have this setting default to -1 in the php.ini we ship with MapGuide.

Tuesday, 3 November 2015

MapGuide tidbits: Improving bundled PHP performance

NOTE: This tip only applies to PHP that is bundled with MapGuide Open Source 2.6 and newer.

MapGuide Open Source 2.6 and newer bundles the 5.5.x series of PHP, which includes a new opcode caching feature. Opcode caching can reduce execution times by upwards of 50% (as is the case with our mapguide-rest functional test suite which usually took around 6 minutes to run, but now runs in just over 3 minutes with opcode caching enabled)

In the interest of playing it safe, we didn't activate this feature in the default php.ini that's bundled with MapGuide. So if you want to activate this feature, you'll have to edit php.ini and add the following lines:

;Enable the opcode caching extensions
zend_extension=php_opcache.dll
;On Linux, the path should be
;zend_extension=/usr/local/mapguideopensource-x.y.z/webserverextensions/php/lib/php/extensions/no-debug-zts-20121212/opcache.so

;Some default opcode caching options. Adjust as necessary
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=1

Save the file (and restart the web server to re-jig PHP if required) and enjoy your faster PHP scripts!

UPDATE (18 Nov): Upon further testing, I've been finding that PHP opcache is horribly unstable on Windows (see this and this). So enable this feature at your own peril!

Thursday, 14 August 2014

Using PHP composer with MapGuide's bundled PHP

If you're building PHP applications for MapGuide (like I am currently doing with mapguide-rest) you should strongly consider using PHP composer for installing and managing all your external PHP libraries.

PHP composer is basically the PHP version of NuGET (for .net) and simplifies and accelerates development of PHP applications by allowing you to install and manage third-party PHP libraries and frameworks with ease.

No dependency hell or wondering what PHP files to include/require. PHP composer will sort all of that out, leaving you to simply "require 'vendor/autoload.php'" to start using your libraries and like NuGet.org, the Packagist website will help you find the PHP library you need to solve your particular problem.

So how do you make sure when installing PHP composer that it will work with MapGuide's bundled PHP? In the windows installer, simply set the PHP path to where MapGuide's PHP is installed.


For Linux or if you didn't use the Windows installer, you should make sure the php that is running when you invoke composer is the one from your MapGuide installation and not a system-based PHP if you so happen to have one installed.

Once installed, you can start to enjoy a (less painful and more exciting) PHP development experience.