Skip to content

Instantly share code, notes, and snippets.

@robfrawley
Last active February 11, 2023 22:36
Show Gist options
  • Save robfrawley/5e4851c27c62026955bb54fa66b4755e to your computer and use it in GitHub Desktop.
Save robfrawley/5e4851c27c62026955bb54fa66b4755e to your computer and use it in GitHub Desktop.
Download PHP Versions (Ubuntu)
#!/usr/bin/env zsh
##
## GLOBAL VARIABLE DEFINITIONS
##
declare -a PHP_VER_LIST_DISABLED=(
'5.6'
)
declare -a PHP_VER_LIST_DEFAULTS=("${(@f)$(
apt-cache search php 2> /dev/null \
| grep -E -o '\bphp[0-9\.]{3}' 2> /dev/null \
| sort \
| uniq \
| grep -oE '[0-9\.]{3}' 2> /dev/null
)}")
declare -a SUDO_OPT_LIST=(
'-p' 'Enter the %u user password to continue with the requested installation or removal operations...'
)
declare -A APT_OPT_LIST=(
[install]='-qqq -y'
[remove]='-qqq -y --purge'
[autoremove]='-qqq -y --purge'
)
declare -A CMD_ACT_ALTS=(
[i]='install'
[r]='remove'
[u]='remove'
[uninstall]='remove'
[l]='list'
[a]='list'
[available]='list'
[t]='list'
[targets]='list'
)
declare -A CMD_ACT_DESC=(
[install]='Installation'
[remove]='Removal'
[list]='List'
)
declare -a SYS_PKG_LIST=(
imagemagick-6.q16hdri
memcached
sqlite3
mariadb-server
mariadb-client
)
declare -a PHP_PKG_LIST=(
apcu
apcu-bc
ast
bcmath
bz2
cli
curl
decimal
dev
gd
geoip
gmp
gnupg
grpc
igbinary
imagick
inotify
intl
json
lz4
mbstring
mcrypt
memcached
msgpack
mysql
opcache
pgsql
phpdbg
protobuf
ps
pspell
readline
rrd
sqlite3
ssh2
xdebug
xml
xmlrpc
xsl
yaml
zip
zstd
)
declare -A PHP_PKG_VERS=(
[5.6]=''
[7.0]=''
[7.1]=''
[7.2]=''
[7.3]=''
[7.4]=''
[8.0]=''
[8.1]=''
[8.2]=''
)
##
## INTERNAL VARIABLE DEFINITIONS
##
declare _SDO_CALL_EXE="$(command -v sudo)"
declare _APT_CALL_EXE="$(command -v apt)"
declare -a _APT_OPTS_EXE=()
declare _APT_CALL_STR=''
declare _APT_CALL_LEN=0
##
## GLOBAL FUNCTION DEFINITIONS
##
function print_wrap()
{
printf -- "${@}"
}
function str_repeat()
{
local str="${1}"
local len="${2:-1}"
for i in $(seq 1 ${len}); do
print_wrap "${str}"
done
}
function str_pluralize()
{
local size="${1}"
local base="${2}"
local plur="${3:-${base}}s"
if [[ ${size} -eq 1 ]]; then
print_wrap '%s' "${base}"
else
print_wrap '%s' "${plur}"
fi
}
function str_explode_on_regex()
{
local value="${1}"
local regex_s="${2}"
local regex_r="${3:-\n}"
sed -E 's/'"${regex_s}"'/'"${regex_r}"'/g' \
<<< "${value}" 2> /dev/null
}
function str_explode_on_spaces()
{
str_explode_on_regex "${1}" "\b[ ]+\b" "\n"
}
function arr_join()
{
local separator="${1}"
local str_quote="${2:-1}"
sed -E 's/'"${separator}"'$//g' <<< $(
sed -E 's/[ ]+/'"${separator}"'/g' <<< "$(
[[ ${str_quote} -eq 1 ]] \
&& print_wrap '"%s" ' "${@:3}" \
|| print_wrap '%s ' "${@:3}"
)"
)
}
function arr_join_on_spaced_comma()
{
arr_join ', ' 0 "${@}"
}
function arr_join_quoted_on_spaced_comma()
{
arr_join ', ' 1 "${@}"
}
function write_text()
{
print_wrap "${@}"
}
function write_newl()
{
local num="${1:-1}"
for i in {1.."${num}"}; do
write_text "\n"
done
}
function write_line()
{
write_text "${1:-}\n" "${@:2}"
}
function write_capt()
{
local package_list="${@}"
write_line '>> %s %s' "${_APT_CALL_STR}" "$(
write_text '%s' "${package_list[*]}" \
| fold -s -w $(($(tput cols)-$((4+_APT_CALL_LEN)))) \
| head -n1
)"
write_text '%s' "${package_list[*]}" \
| fold -s -w $(($(tput cols)-$((4+_APT_CALL_LEN)))) \
| sed -E '1d; s/^/>> '"$(str_repeat ' ' ${_APT_CALL_LEN})"' /g'
}
function write_list()
{
write_line '-- %s' "${@}"
}
function write_line_type_fail()
{
1>&2 write_newl
1>&2 write_text '!! '
1>&2 write_line "${@:2}"
1>&2 write_newl
exit "${1}"
}
function write_line_type_warn()
{
1>&2 write_newl
1>&2 write_text '!! '
1>&2 write_line "${@}"
}
function write_line_type_head()
{
write_newl
write_text '## '
write_line "${@}"
}
function exists_in_list()
{
local find="${1}"
local -a list=("${@:2}")
write_line '%s' "${(@v)list}" \
| grep -oE '^'"${find}"'$' &> /dev/null
}
function exists_in_list_and_non_empty()
{
exists_in_list "${@}" && [[ ${#} -gt 1 ]]
}
function resolve_real_pkg_name()
{
local package="${1}"
local version="${2}"
write_text 'php%s-%s' "${version}" "${package}"
}
function is_pkg_available()
{
local p="${1}"
apt info "${p}" 2> /dev/null \
| grep -oE '^Version:' &> /dev/null
}
function is_php_ver_installed()
{
local v="${1}"
dpkg -l 2> /dev/null \
| grep -Eo '^ii[ ]+php'"${v}"'[^ ]+' &> /dev/null
}
function build_and_filter_pkg_list()
{
local v="${1}"
for p in "${@:2}"; do
local r="$(resolve_real_pkg_name "${p}" "${v}")"
if is_pkg_available "${r}"; then
write_line "${r}"
fi
done
}
function resolve_php_act_name()
{
local name="${1}"
for a r in "${(@vk)CMD_ACT_ALTS}"; do
if [[ "${name}" == "${a}" ]]; then
name="$(resolve_php_act_name "${r}")"
fi
done
write_text '%s' "${name}"
}
function resolve_php_pkg_list__adls()
{
local v="${1}"
if exists_in_list_and_non_empty "${v}" "${(@k)PHP_PKG_VERS}"; then
build_and_filter_pkg_list "${v}" "${(@f)$(
str_explode_on_spaces "${PHP_PKG_VERS[${v}]}"
)}"
fi
}
function resolve_php_pkg_list__base()
{
local v="${1}"
build_and_filter_pkg_list "${v}" "${(@v)PHP_PKG_LIST}"
}
function resolve_php_pkg_list()
{
local version="${1}"
local -a pkglist=("${(@f)$(
sort <<< "$(
resolve_php_pkg_list__base "${version}"
resolve_php_pkg_list__adls "${version}"
)" | uniq
)}")
write_line '%s' "${(@v)pkglist}"
}
function resolve_sys_pkg_list__autoinstalled()
{
"${_SDO_CALL_EXE}" "${(@v)SUDO_OPT_LIST}" "${_APT_CALL_EXE}" autoremove --dry-run 2> /dev/null \
| grep -Po '^Remv \K[^ ]+' 2> /dev/null
}
function update_sys_pkgs__rmtcache()
{
write_line_type_head 'Updating Package Cache...'
if ! "${_SDO_CALL_EXE}" "${(@v)SUDO_OPT_LIST}" "${_APT_CALL_EXE}" update -qqq; then
write_line_type_fail 3 'Failed to update package cache!'
fi
}
function remove_sys_pkgs__autoinst()
{
local -a del_packages=($(resolve_sys_pkg_list__autoinstalled))
if [[ ${#del_packages[@]} -le 0 ]]; then
return
fi
write_line_type_head \
'System Auto-Installation %s (%d %s)' \
"$(str_pluralize "${#del_packages[@]}" "Removal")" \
"${#del_packages[@]}" \
"$(str_pluralize "${#del_packages[@]}" "package")"
write_list "${(@v)del_packages}"
write_line '>> %s %s autoremove %s' \
"${_SDO_CALL_EXE}" \
"${_APT_CALL_EXE}" \
"${APT_OPT_LIST[autoremove]}"
if ! "${_SDO_CALL_EXE}" "${(@v)SUDO_OPT_LIST}" "${_APT_CALL_EXE}" autoremove $(
str_explode_on_spaces "${APT_OPT_LIST[autoremove]}"
); then
write_line_type_fail 2 'Failed to successfully complete requested operations!'
fi
}
function remove_sys_pkgs__depslist()
{
local name="${1}"
local -a list=("${@:2}")
if [[ ${#list[@]} -ne ${#PHP_VER_LIST_DEFAULTS[@]} ]] && [[ ${name} != 'install' ]]; then
return
fi
write_line_type_head \
'System Package %s Command (%d %s)' \
"${CMD_ACT_DESC[${name}]}" \
"${#SYS_PKG_LIST[@]}" \
"$(str_pluralize "${#SYS_PKG_LIST[@]}" "package")"
write_list "${(@v)SYS_PKG_LIST}"
write_capt "${(@v)SYS_PKG_LIST}"
if ! "${_SDO_CALL_EXE}" "${(@v)SUDO_OPT_LIST}" "${_APT_CALL_EXE}" "${(@v)_APT_OPTS_EXE}" "${(@v)SYS_PKG_LIST}"; then
write_line_type_fail 2 'Failed to successfully complete requested operations!'
fi
}
function output_php_vers__listings()
{
write_line_type_head \
'Listing available PHP targets (%d %s)' \
"${#PHP_VER_LIST_DEFAULTS[@]}" \
"$(str_pluralize "${#PHP_VER_LIST_DEFAULTS[@]}" "version")"
write_list "${(@v)PHP_VER_LIST_DEFAULTS}"
write_newl
exit 0
}
function output_php_vers__subjects()
{
local name="${1}"
local -a list=("${@:2}")
write_line_type_head \
'Performing PHP %s (%d %s)' \
"$(str_pluralize "${#list[@]}" "${CMD_ACT_DESC[${name}]}")" \
"${#list[@]}" \
"$(str_pluralize "${#list[@]}" "version")"
write_list "${(@v)list}"
}
function resolve_apt_opt_list()
{
local name="${1}"
sed -E 's/\b[ ]+\B/\n/g' <<< "${APT_OPT_LIST[${name}]}" 2> /dev/null
}
function main()
{
if command -v ssh-askpass &> /dev/null; then
export SUDO_ASKPASS="$(command -v ssh-askpass)"
SUDO_OPT_LIST+=("--askpass")
fi
local php_act_name="$(resolve_php_act_name "${1:-install}")"
local -a php_ver_list=()
local php_ver_temp=''
local php_ver_posn=0
local php_ver_opts=0
if [[ "${php_act_name}" == 'list' ]]; then
output_php_vers__listings
fi
for v in "${@:2}"; do
for s in "${(@v)php_ver_list}"; do
if [[ "${v}" == "${s}" ]]; then
continue 2
fi
done
php_ver_opts=$((php_ver_opts + 1))
for s in "${(@v)PHP_VER_LIST_DEFAULTS}"; do
if [[ ${v} == ${s} ]]; then
php_ver_list+="${v}"
continue 2
fi
done
write_line_type_warn 'Ignoring invalid specified PHP version "%s"...' "${v}"
done
if [[ ${php_ver_opts} -eq 0 ]]; then
for v in "${(@v)PHP_VER_LIST_DEFAULTS}"; do
for b in "${(@v)PHP_VER_LIST_DISABLED}"; do
if [[ "${v}" == "${b}" ]] && [[ "${php_act_name}" != 'remove' ]]; then
continue 2
fi
done
php_ver_list+=("${v}")
done
fi
php_ver_temp="${(@v)php_ver_list}"
php_ver_list=($(
str_explode_on_spaces "${php_ver_temp}" \
| sort --general-numeric-sort \
| uniq
))
if [[ ${#php_ver_list[@]} -eq 0 ]]; then
write_line_type_fail 3 \
'No valid PHP versions specified (use one of %s)...' \
"$(arr_join_quoted_on_spaced_comma "${(@v)PHP_VER_LIST_DEFAULTS}")"
fi
if ! exists_in_list_and_non_empty "${php_act_name:-x}" "${(@k)APT_OPT_LIST}"; then
write_line_type_fail 3 \
'Invalid action type of "%s" (use one of %s)' \
"${php_act_name}" \
"$(arr_join_quoted_on_spaced_comma "${(@k)APT_OPT_LIST}")"
fi
_APT_OPTS_EXE=("${php_act_name}" $(resolve_apt_opt_list "${php_act_name}"))
_APT_CALL_STR="$(write_line '%s' "${_SDO_CALL_EXE} ${_APT_CALL_EXE} ${_APT_OPTS_EXE[*]}")"
_APT_CALL_LEN="${#_APT_CALL_STR}"
update_sys_pkgs__rmtcache
remove_sys_pkgs__autoinst
output_php_vers__subjects "${php_act_name}" "${(@v)php_ver_list}"
for v in "${(@v)php_ver_list}"; do
php_ver_posn=$((php_ver_posn + 1))
write_newl
write_text '## PHP %s %d/%d [%s] ' \
"${CMD_ACT_DESC[${php_act_name}]}" \
"${php_ver_posn}" \
"${#php_ver_list[@]}" \
"${v}"
if [[ ${php_act_name} == 'remove' ]] && ! is_php_ver_installed "${v}"; then
write_newl
write_line '-- Skipping (no matching installed packages found for version "%s")...' "${v}"
continue
fi
local -a package_list=("${(@f)$(resolve_php_pkg_list "${v}")}")
write_line '(%d %s)' "${#package_list[@]}" "$(str_pluralize "${#package_list[@]}" "package")"
write_list "${(@v)package_list}"
write_capt "${(@v)package_list}"
write_newl
if ! "${_SDO_CALL_EXE}" "${(@v)SUDO_OPT_LIST}" "${_APT_CALL_EXE}" "${(@v)_APT_OPTS_EXE}" "${(@v)package_list}"; then
write_line_type_fail 2 'Failed to successfully complete requested operations!'
fi
done
remove_sys_pkgs__depslist "${php_act_name}" "${(@v)php_ver_list}"
remove_sys_pkgs__autoinst
write_line_type_head 'Completed all operations...'
write_newl
}
##
## CALL MAIN FUNCTION
##
main "${@}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment