#!/bin/sh

# This script wraps docker-php-ext-install, properly configuring the system.
#
# Copyright (c) Michele Locati, 2018
#
# Source: https://github.com/mlocati/docker-php-extension-installer
#
# License: MIT - see https://github.com/mlocati/docker-php-extension-installer/blob/master/LICENSE

# Let's set a sane environment
set -o errexit
set -o nounset

# Reset the Internal Field Separator
resetIFS () {
	IFS='	 
'
}

# Get the PHP Major-Minor version as an integer value, in format MMmm (example: 506 for PHP 5.6.15)
#
# Output:
#   The PHP numeric Major-Minor version
getPHPMajorMinor () {
	php -r '$v = explode(".", PHP_VERSION); echo $v[0] * 100 + $v[1];'
}

# Get the normalized list of already installed PHP modules
#
# Output:
#   Space-separated list of module handles
getPHPInstalledModules () {
	getPHPInstalledModules_result=''
	IFS='
'
	for getPHPInstalledModules_module in $(php -m)
	do
		getPHPInstalledModules_moduleNormalized=''
		case "${getPHPInstalledModules_module}" in
			\[PHP\ Modules\])
				;;
			\[Zend\ Modules\])
				break
				;;
			Core|PDO|PDO_*|Phar|Reflection|SimpleXML|SPL|SQLite|Xdebug)
				getPHPInstalledModules_moduleNormalized=$(LC_CTYPE=C printf '%s' "${getPHPInstalledModules_module}" | tr '[:upper:]' '[:lower:]')
				;;
			Zend\ OPcache)
				getPHPInstalledModules_moduleNormalized='opcache'
				;;
			*\ *|A*|*B*|*C*|*D*|*E*|*F*|*G*|*H*|*I*|*J*|*K*|*L*|*M*|*N*|*O*|*P*|*Q*|*R*|*S*|*T*|*U*|*V*|*W*|*X*|*Y*|*Z*)
				printf '### WARNING Unrecognized module name: %s ###\n' "${getPHPInstalledModules_module}" >&2
				;;
			*)
				getPHPInstalledModules_moduleNormalized="${getPHPInstalledModules_module}"
				;;
		esac
		if test -n "${getPHPInstalledModules_moduleNormalized}"
		then
			if ! stringInList "${getPHPInstalledModules_moduleNormalized}" "${getPHPInstalledModules_result}"
			then
				getPHPInstalledModules_result="${getPHPInstalledModules_result} ${getPHPInstalledModules_moduleNormalized}"
			fi
		fi
	done
	resetIFS
	printf '%s' "${getPHPInstalledModules_result}"
}

# Get the handles of the modules to be installed
#
# Arguments:
#   $@: all module handles
# Output:
#   Space-separated list of module handles
getModulesToInstall () {
	getModulesToInstall_alreadyInstalled="$(getPHPInstalledModules)"
	getModulesToInstall_result=''
	while :
	do
		if test $# -lt 1
		then
			break
		fi
		if stringInList "${1}" "${getModulesToInstall_result}"
		then
			printf '### WARNING Duplicated module name specified: %s ###\n' "${1}" >&2
		elif stringInList "${1}" "${getModulesToInstall_alreadyInstalled}"
		then
			printf '### WARNING Module already installed: %s ###\n' "${1}" >&2
		else
			getModulesToInstall_result="${getModulesToInstall_result} ${1}"
		fi
		shift
	done
	printf '%s' "${getModulesToInstall_result}"
}

# Get the required APT packages for a specific PHP version and for the list of module handles
#
# Arguments:
#   $1: the numeric PHP Major-Minor version
#   $@: the PHP module handles
#
# Output:
#   Space-separated list of APT packages
getRequiredAptPackages () {
	getRequiredAptPackages_result=''
	getRequiredAptPackages_phpv=${1}
	while :
	do
		if test $# -lt 2
		then
			break
		fi
		shift
		case "${1}" in
			bz2)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libbz2-dev"
				;;
			cmark)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} cmake"
				;;
			enchant)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libenchant-dev"
				;;
			gd)
				if test $getRequiredAptPackages_phpv -lt 700
				then
					getRequiredAptPackages_result="${getRequiredAptPackages_result} libvpx-dev libjpeg62-turbo-dev libpng-dev libxpm-dev libfreetype6-dev"
				else
					getRequiredAptPackages_result="${getRequiredAptPackages_result} libwebp-dev libjpeg62-turbo-dev libpng-dev libxpm-dev libfreetype6-dev"
				fi
				;;
			gmp)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libgmp-dev"
				;;
			imap)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libc-client-dev libkrb5-dev"
				;;
			interbase)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} firebird-dev libib-util"
				;;
			imagick)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libmagickwand-dev"
				;;
			intl)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libicu-dev"
				;;
			ldap)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libldap2-dev"
				;;
			memcache)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} zlib1g-dev"
				;;
			memcached)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libmemcached-dev zlib1g-dev"
				;;
			mcrypt)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libmcrypt-dev"
				;;
			mssql)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} freetds-dev"
				;;
			odbc)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} unixodbc-dev"
				;;
			pdo_dblib)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} freetds-dev"
				;;
			pdo_firebird)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} firebird-dev libib-util"
				;;
			pdo_pgsql)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libpq-dev"
				;;
			pdo_odbc)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} unixodbc-dev"
				;;
			pdo_sqlsrv)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} unixodbc-dev"
				;;
			pgsql)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libpq-dev"
				;;
			pspell)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libpspell-dev"
				;;
			recode)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} librecode-dev"
				;;
			ssh2)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libssh2-1-dev"
				;;
			snmp)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} snmp libsnmp-dev"
				;;
			soap)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libxml2-dev"
				;;
			solr)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libcurl4-gnutls-dev libxml2-dev"
				;;
			sqlsrv)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} unixodbc-dev"
				;;
			sybase_ct)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} freetds-dev"
				;;
			tidy)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libtidy-dev"
				;;
			uuid)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} uuid-dev"
				;;
			wddx)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libxml2-dev"
				;;
			xmlrpc)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libxml2-dev"
				;;
			xsl)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libxslt-dev"
				;;
			yaml)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} libyaml-dev"
				;;
			zip)
				getRequiredAptPackages_result="${getRequiredAptPackages_result} zlib1g-dev"
				;;
		esac
	done
	printf '%s' "${getRequiredAptPackages_result}"
}

# Install a bundled PHP module given its handle
#
# Arguments:
#   $1: the numeric PHP Major-Minor version
#   $2: the handle of the PHP module
installBundledModule () {
	printf '### INSTALLING BUNDLED MODULE %s ###\n' "${2}"
	case "${2}" in
		gd)
			if test $1 -lt 700
			then
				docker-php-ext-configure gd --with-gd --with-vpx-dir --with-jpeg-dir --with-png-dir --with-zlib-dir --with-xpm-dir --with-freetype-dir --enable-gd-native-ttf
			elif test $1 -lt 702
			then
				docker-php-ext-configure gd --with-gd --with-webp-dir --with-jpeg-dir --with-png-dir --with-zlib-dir --with-xpm-dir --with-freetype-dir --enable-gd-native-ttf
			else
				docker-php-ext-configure gd --with-gd --with-webp-dir --with-jpeg-dir --with-png-dir --with-zlib-dir --with-xpm-dir --with-freetype-dir
			fi
			;;
		gmp)
			case "$1" in
				506)
					if ! test -f /usr/include/gmp.h
					then
						ln -s /usr/include/x86_64-linux-gnu/gmp.h /usr/include/gmp.h
					fi
					;;
			esac
			;;
		imap)
			docker-php-ext-configure imap --with-kerberos --with-imap-ssl
			;;
		ldap)
			docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu
			;;
		mssql|pdo_dblib)
			case "$1" in
				506|700|701|702)
					if ! test -f /usr/lib/libsybdb.so
					then
						ln -s /usr/lib/x86_64-linux-gnu/libsybdb.so /usr/lib/libsybdb.so
					fi
					;;
			esac
			;;
		odbc)
			case "$1" in
				506|700|701|702)
					docker-php-source extract
					cd /usr/src/php/ext/odbc
					phpize
					sed -ri 's@^ *test +"\$PHP_.*" *= *"no" *&& *PHP_.*=yes *$@#&@g' configure
					./configure --with-unixODBC=shared,/usr
					cd -
					;;
			esac
			;;
		pdo_odbc)
			docker-php-ext-configure pdo_odbc --with-pdo-odbc=unixODBC,/usr
			;;
		sybase_ct)
			docker-php-ext-configure sybase_ct --with-sybase-ct=/usr
			;;
	esac
	docker-php-ext-install "${2}"
}

# Fetch a tar.gz file, extract it and returns the path of the extracted folder.
#
# Arguments:
#   $1: the URL of the file to be downloaded
#
# Output:
#   The path of the extracted directory
getPackageSource () {
	mkdir -p /tmp/src
	getPackageSource_tempFile=$(mktemp -p /tmp/src)
	curl -L -s -S -o "${getPackageSource_tempFile}" "$1"
	getPackageSource_tempDir=$(mktemp -p /tmp/src -d)
	cd "${getPackageSource_tempDir}"
	tar -xzf "${getPackageSource_tempFile}"
	cd - >/dev/null
	unlink "${getPackageSource_tempFile}"
	getPackageSource_outDir=''
	for getPackageSource_i in $(ls "${getPackageSource_tempDir}")
	do
		if test -n "${getPackageSource_outDir}" -o -f "${getPackageSource_tempDir}/${getPackageSource_i}"
		then
			getPackageSource_outDir=''
			break
		fi
		getPackageSource_outDir="${getPackageSource_tempDir}/${getPackageSource_i}"
	done
	if test -n "${getPackageSource_outDir}"
	then
		printf '%s' "${getPackageSource_outDir}"
	else
		printf '%s' "${getPackageSource_tempDir}"
	fi
}

# Install a PHP module given its handle from source code
#
# Arguments:
#   $1: the handle of the PHP module
#   $2: the URL of the module source code
#   $3: the options of the configure command
installModuleFromSource () {
	printf '### INSTALLING MODULE %s FROM SOURCE CODE ###\n' "${1}"
	installModuleFromSource_dir="$(getPackageSource "${2}")"
	cd "${installModuleFromSource_dir}"
	phpize
	./configure ${3}
	make -j$(nproc) install
	cd --
	docker-php-ext-enable "${1}"
}

# Install a PECL PHP module given its handle
#
# Arguments:
#   $1: the numeric PHP Major-Minor version
#   $2: the handle of the PHP module
installPECLModule () {
	printf '### INSTALLING PECL MODULE %s ###\n' "${2}"
	installPECLModule_actual="${2}"
	case "${2}" in
		memcached)
			if test $1 -lt 700
			then
				installPECLModule_actual="${2}-2.2.0"
			fi
			;;
		pthreads)
			if test $1 -lt 700
			then
				installPECLModule_actual="${2}-2.0.10"
			fi
			;;
		ssh2)
			if test $1 -ge 700
			then
				installPECLModule_actual="${2}-1.1.2"
			fi
			;;
		xdebug)
			if test $1 -lt 501
			then
				installPECLModule_actual="${2}-2.0.5"
			elif test $1 -lt 504
			then
				installPECLModule_actual="${2}-2.2.7"
			elif test $1 -lt 505
			then
				installPECLModule_actual="${2}-2.4.1"
			elif test $1 -lt 700
			then
				installPECLModule_actual="${2}-2.5.5"
			fi
			;;
		yaml)
			if test $1 -lt 700
			then
				installPECLModule_actual="${2}-1.3.1"
			fi
			;;
	esac
	if test "${2}" != "${installPECLModule_actual}"
	then
		printf '  (installing version %s)\n' "${installPECLModule_actual}"
	fi
	pecl install "${installPECLModule_actual}"
	docker-php-ext-enable "${2}"
}

# Check if a string is in a list of space-separated string
#
# Arguments:
#   $1: the string to be checked
#   $2: the string list
#
# Return:
#   0 (true): if the string is in the list
#   1 (false): if the string is not in the list
stringInList () {
	for stringInList_listItem in ${2}
	do
		if test "${1}" = "${stringInList_listItem}"
		then
			return 0
		fi
	done
	return 1
}

resetIFS
TEMPORARY_DIRS=''
PHP_MAJMIN_VERSION=$(getPHPMajorMinor)
case "${PHP_MAJMIN_VERSION}" in
	506|700|701|702)
		;;
	*)
		printf "### ERROR: Unsupported PHP version: %s.%s ###\n" $(( PHP_MAJMIN_VERSION / 100 )) $(( PHP_MAJMIN_VERSION % 100 ))
esac
PHP_MODULES_TO_INSTALL=$(getModulesToInstall "$@")
if test -n "${PHP_MODULES_TO_INSTALL}"
then
	REQUIRED_APT_PACKAGES=$(getRequiredAptPackages ${PHP_MAJMIN_VERSION} ${PHP_MODULES_TO_INSTALL})
	if test -n "${REQUIRED_APT_PACKAGES}"
	then
		printf '### INSTALLING REQUIRED APT PACKAGES ###\n'
		DEBIAN_FRONTEND=noninteractive apt-get update -y
		DEBIAN_FRONTEND=noninteractive apt-get install -y ${REQUIRED_APT_PACKAGES}
	fi
	docker-php-source extract
	BUNDLED_MODULES="$(find /usr/src/php/ext -mindepth 2 -maxdepth 2 -type f -name 'config.m4' | xargs -n1 dirname | xargs -n1 basename | xargs)"
	for PHP_MODULE_TO_INSTALL in ${PHP_MODULES_TO_INSTALL}
	do
		if stringInList "${PHP_MODULE_TO_INSTALL}" "${BUNDLED_MODULES}"
		then
			installBundledModule ${PHP_MAJMIN_VERSION} "${PHP_MODULE_TO_INSTALL}"
		else
			MODULE_SOURCE=''
			MODULE_SOURCE_CONFIGOPTIONS=''
			case "${PHP_MODULE_TO_INSTALL}" in
				cmark)
					MODULE_SOURCE=https://github.com/krakjoe/cmark/archive/v1.0.0.tar.gz
					cd "$(getPackageSource https://github.com/commonmark/cmark/archive/0.28.3.tar.gz)"
					make install
					cd -
					MODULE_SOURCE_CONFIGOPTIONS=--with-cmark
					;;
			esac
			if test -n "${MODULE_SOURCE}"
			then
				installModuleFromSource "${PHP_MODULE_TO_INSTALL}" "${MODULE_SOURCE}" "${MODULE_SOURCE_CONFIGOPTIONS}"
			else
				installPECLModule ${PHP_MAJMIN_VERSION} "${PHP_MODULE_TO_INSTALL}"
			fi
		fi
	done
fi

docker-php-source delete
rm -rf /tmp/pear
rm -rf /var/lib/apt/lists/*
rm -rf /tmp/src