Passed
Push — main ( b7799b...6c7a2f )
by Bjarn
03:09 queued 01:45
created

PhpExtension.enable   A

Complexity

Conditions 2

Size

Total Lines 14
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 13
dl 0
loc 14
rs 9.75
c 0
b 0
f 0
1
import execa from 'execa'
2
import {existsSync} from 'fs'
3
import * as fs from 'fs'
4
5
abstract class PhpExtension {
6
    static NORMAL_EXTENSION_TYPE = 'extension'
7
    static ZEND_EXTENSION_TYPE = 'zend_extension'
8
9
    abstract extension: string
10
    abstract alias: string
11
12
    // Extension settings
13
    default: boolean = true
14
    extensionType: string = PhpExtension.NORMAL_EXTENSION_TYPE
15
16
    /**
17
     * Get the path of the PHP ini currently used by PECL.
18
     */
19
    getPhpIni = async (): Promise<string> => {
20
        const peclIni = await execa('pecl', ['config-get', 'php_ini'])
21
        const peclIniPath = peclIni.stdout.replace('\n', '')
22
23
        if (existsSync(peclIniPath))
24
            return peclIniPath
25
26
        const phpIni = await execa('php', ['-i', '|', 'grep', 'php.ini'])
27
28
        const matches = phpIni.stdout.match(/Path => ([^\s]*)/)
29
30
        if (!matches || matches.length <= 0)
31
            throw new Error('Unable to find php.ini.')
32
33
        return `${matches[1].trim()}/php.ini`
34
    }
35
36
    /**
37
     * Check if the extension is enabled.
38
     */
39
    isEnabled = async (): Promise<boolean> => {
40
        const {stdout} = await execa('php', ['-m', '|', 'grep', this.extension])
41
        const extensions = stdout.split('\n')
42
43
        return extensions.includes(this.extension)
44
    }
45
46
    /**
47
     * Check if the extension is installed.
48
     */
49
    isInstalled = async (): Promise<boolean> => {
50
        const {stdout} = await execa('pecl', ['list', '|', 'grep', this.extension])
51
        return stdout.includes(this.extension)
52
    }
53
54
    /**
55
     * Install the extension.
56
     */
57
    install = async (): Promise<boolean> => {
58
        if (await this.isInstalled()) {
59
            console.log(`Extension ${this.extension} is already installed.`)
60
            return false
61
        }
62
63
        const {stdout} = await execa('pecl', ['install', this.extension])
64
65
        const installRegex = new RegExp(`Installing '(.*${this.alias}.so)'`, 'g').test(stdout)
66
        if (!installRegex)
67
            throw new Error(`Unable to find installation path for ${this.extension}. Result:\n\n`)
68
69
        if (stdout.includes('Error:'))
70
            throw new Error(`Found installation path, but installation still failed: \n\n${stdout}`)
71
72
        const phpIniPath = await this.getPhpIni()
73
        let phpIni = await fs.readFileSync(phpIniPath, 'utf-8')
74
75
        // TODO: Fix duplicate extension entires in php.ini
76
        const extensionRegex = new RegExp(`(zend_extension|extension)="(.*${this.alias}.so)"`, 'g').test(phpIni)
77
        if (!extensionRegex)
78
            throw new Error(`Unable to find definition in ${phpIniPath} for ${this.extension}`)
79
80
        console.log(`Extension ${this.extension} has been installed.`)
81
        return true
82
    }
83
84
    /**
85
     * Uninstall the extension.
86
     */
87
    uninstall = async (): Promise<void> => {
88
        await execa('pecl', ['uninstall', this.extension])
89
    }
90
91
    /**
92
     * Enable the extension.
93
     */
94
    enable = async (): Promise<void> => {
95
        if (await this.isEnabled()) {
96
            console.log(`Extension ${this.extension} is already enabled.`)
97
        }
98
99
        const phpIniPath = await this.getPhpIni()
100
        let phpIni = await fs.readFileSync(phpIniPath, 'utf-8')
101
        const regex = new RegExp(`(zend_extension|extension)="(.*${this.alias}.so)"\/n`, 'g')
102
        phpIni = phpIni.replace(regex, '')
103
        phpIni = `${this.extensionType}="${this.alias}.so"\n${phpIni}`
104
105
        await fs.writeFileSync(phpIniPath, phpIni)
106
107
        console.log(`Extension ${this.extension} has been enabled`)
108
    }
109
110
    /**
111
     * Disable the extension.
112
     */
113
    disable = async (): Promise<boolean> => {
114
        const phpIniPath = await this.getPhpIni()
115
        let phpIni = await fs.readFileSync(phpIniPath, 'utf-8')
116
117
        const regex = new RegExp(`;?(zend_extension|extension)=".*${this.alias}.so"\n`, 'g')
118
        phpIni = phpIni.replace(regex, '')
119
120
        await fs.writeFileSync(phpIniPath, phpIni)
121
122
        console.log(`Extension ${this.extension} has been disabled`)
123
124
        return true
125
    }
126
}
127
128
export default PhpExtension