diff --git a/composer.json b/composer.json
index 5608608666109344841dba7e6cdd1092ffa85dc0..392f934e03ba0bc9f2da0f26a78101bd210a687a 100644
--- a/composer.json
+++ b/composer.json
@@ -74,16 +74,16 @@
     ],
     "scripts": {
         "pre-update-cmd": [
-            "misc/composer/clean-xhprof.sh"
+            "Piwik\\Composer\\ScriptHandler::cleanXhprof"
         ],
         "pre-install-cmd": [
-            "misc/composer/clean-xhprof.sh"
+            "Piwik\\Composer\\ScriptHandler::cleanXhprof"
         ],
         "post-update-cmd": [
-            "misc/composer/build-xhprof.sh"
+            "Piwik\\Composer\\ScriptHandler::buildXhprof"
         ],
         "post-install-cmd": [
-            "misc/composer/build-xhprof.sh"
+            "Piwik\\Composer\\ScriptHandler::buildXhprof"
         ]
     }
 }
diff --git a/core/Composer/ScriptHandler.php b/core/Composer/ScriptHandler.php
new file mode 100644
index 0000000000000000000000000000000000000000..d4db6536b0125aeae8a5a80effd99fe2f0e947b2
--- /dev/null
+++ b/core/Composer/ScriptHandler.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Composer;
+
+/**
+ * Scripts executed before/after Composer install and update.
+ *
+ * We use this PHP class because setting the bash scripts directly in composer.json breaks
+ * Composer on Windows systems.
+ */
+class ScriptHandler
+{
+    public static function cleanXhprof()
+    {
+        if (! is_dir('vendor/facebook/xhprof/extension')) {
+            return;
+        }
+
+        passthru('misc/composer/clean-xhprof.sh');
+    }
+
+    public static function buildXhprof()
+    {
+        if (! is_dir('vendor/facebook/xhprof/extension')) {
+            return;
+        }
+
+        passthru('misc/composer/build-xhprof.sh');
+    }
+}