5
5
namespace PhpSchool \PhpWorkshop \Check ;
6
6
7
7
use PDO ;
8
- use PDOException ;
8
+ use PhpSchool \ PhpWorkshop \ Event \ CgiExerciseRunnerEvent ;
9
9
use PhpSchool \PhpWorkshop \Event \CliExecuteEvent ;
10
+ use PhpSchool \PhpWorkshop \Event \CliExerciseRunnerEvent ;
10
11
use PhpSchool \PhpWorkshop \Event \Event ;
11
12
use PhpSchool \PhpWorkshop \Event \EventDispatcher ;
12
- use PhpSchool \PhpWorkshop \Exercise \ TemporaryDirectoryTrait ;
13
+ use PhpSchool \PhpWorkshop \Event \ ExerciseRunnerEvent ;
13
14
use PhpSchool \PhpWorkshop \ExerciseCheck \DatabaseExerciseCheck ;
14
15
use PhpSchool \PhpWorkshop \Result \Failure ;
15
16
use PhpSchool \PhpWorkshop \Result \Success ;
16
- use RuntimeException ;
17
+ use PhpSchool \PhpWorkshop \Utils \Path ;
18
+ use PhpSchool \PhpWorkshop \Utils \System ;
19
+ use Symfony \Component \Filesystem \Filesystem ;
17
20
18
21
/**
19
22
* This check sets up a database and a `PDO` object. It prepends the database DSN as a CLI argument to the student's
23
26
*/
24
27
class DatabaseCheck implements ListenableCheckInterface
25
28
{
26
- use TemporaryDirectoryTrait;
29
+ private Filesystem $ filesystem ;
30
+ private ?string $ dbContent = null ;
27
31
28
- private string $ databaseDirectory ;
29
- private string $ userDatabasePath ;
30
- private string $ solutionDatabasePath ;
31
- private string $ userDsn ;
32
- private string $ solutionDsn ;
33
-
34
- /**
35
- * Setup paths and DSN's.
36
- */
37
- public function __construct ()
32
+ public function __construct (Filesystem $ filesystem = null )
38
33
{
39
- $ this ->databaseDirectory = $ this ->getTemporaryPath ();
40
- $ this ->userDatabasePath = sprintf ('%s/user-db.sqlite ' , $ this ->databaseDirectory );
41
- $ this ->solutionDatabasePath = sprintf ('%s/solution-db.sqlite ' , $ this ->databaseDirectory );
42
- $ this ->solutionDsn = sprintf ('sqlite:%s ' , $ this ->solutionDatabasePath );
43
- $ this ->userDsn = sprintf ('sqlite:%s ' , $ this ->userDatabasePath );
34
+ $ this ->filesystem = $ filesystem ? $ filesystem : new Filesystem ();
44
35
}
45
36
46
37
/**
@@ -64,78 +55,69 @@ public function getExerciseInterface(): string
64
55
*/
65
56
public function attach (EventDispatcher $ eventDispatcher ): void
66
57
{
67
- if (file_exists ($ this ->databaseDirectory )) {
68
- throw new RuntimeException (
69
- sprintf ('Database directory: "%s" already exists ' , $ this ->databaseDirectory ),
70
- );
71
- }
72
-
73
- mkdir ($ this ->databaseDirectory , 0777 , true );
74
-
75
- try {
76
- $ db = new PDO ($ this ->userDsn );
77
- $ db ->setAttribute (PDO ::ATTR_ERRMODE , PDO ::ERRMODE_EXCEPTION );
78
- } catch (PDOException $ e ) {
79
- rmdir ($ this ->databaseDirectory );
80
- throw $ e ;
81
- }
82
-
83
- $ eventDispatcher ->listen ('verify.start ' , function (Event $ e ) use ($ db ) {
84
- /** @var DatabaseExerciseCheck $exercise */
85
- $ exercise = $ e ->getParameter ('exercise ' );
86
- $ exercise ->seed ($ db );
87
- //make a copy - so solution can modify without effecting database user has access to
88
- copy ($ this ->userDatabasePath , $ this ->solutionDatabasePath );
89
- });
58
+ $ eventDispatcher ->listen (['verify.start ' , 'run.start ' ], function (Event $ e ) {
59
+ $ path = System::randomTempPath ('sqlite ' );
90
60
91
- $ eventDispatcher ->listen ('run.start ' , function (Event $ e ) use ($ db ) {
92
- /** @var DatabaseExerciseCheck $exercise */
93
- $ exercise = $ e ->getParameter ('exercise ' );
94
- $ exercise ->seed ($ db );
95
- });
61
+ $ this ->filesystem ->touch ($ path );
62
+
63
+ try {
64
+ $ db = $ this ->getPDO ($ path );
65
+
66
+ /** @var DatabaseExerciseCheck $exercise */
67
+ $ exercise = $ e ->getParameter ('exercise ' );
68
+ $ exercise ->seed ($ db );
96
69
97
- $ eventDispatcher ->listen ('cli.verify.reference-execute.pre ' , function (CliExecuteEvent $ e ) {
98
- $ e ->prependArg ($ this ->solutionDsn );
70
+ $ this ->dbContent = (string ) file_get_contents ($ path );
71
+ } finally {
72
+ unset($ db );
73
+
74
+ $ this ->filesystem ->remove ($ path );
75
+ }
99
76
});
100
77
101
78
$ eventDispatcher ->listen (
102
- ['cli.verify.student-execute.pre ' , 'cli.run.student-execute.pre ' ],
103
- function (CliExecuteEvent $ e ) {
104
- $ e ->prependArg ($ this ->userDsn );
79
+ ['cli.verify.prepare ' , 'cgi.verify.prepare ' ],
80
+ function (CliExerciseRunnerEvent |CgiExerciseRunnerEvent $ e ) {
81
+ $ e ->getScenario ()->withFile ('db.sqlite ' , (string ) $ this ->dbContent );
82
+
83
+ $ this ->dbContent = null ;
105
84
},
106
85
);
107
86
108
- $ eventDispatcher ->insertVerifier ( ' verify.finish ' , function ( Event $ e ) use ( $ db ) {
109
- /** @var DatabaseExerciseCheck $exercise */
110
- $ exercise = $ e ->getParameter ( ' exercise ' );
111
- $ verifyResult = $ exercise -> verify ( $ db );
87
+ $ eventDispatcher ->listen (
88
+ ' cli.verify.reference-execute.pre ' ,
89
+ fn ( CliExecuteEvent $ e ) => $ e ->prependArg ( ' sqlite:db.sqlite ' ),
90
+ );
112
91
113
- if (false === $ verifyResult ) {
114
- return Failure::fromNameAndReason ($ this ->getName (), 'Database verification failed ' );
115
- }
92
+ $ eventDispatcher ->listen (
93
+ ['cli.verify.student-execute.pre ' , 'cli.run.student-execute.pre ' ],
94
+ fn (CliExecuteEvent $ e ) => $ e ->prependArg ('sqlite:db.sqlite ' ),
95
+ );
116
96
117
- return new Success ( ' Database Verification Check ' );
118
- } );
97
+ $ eventDispatcher -> insertVerifier ( ' verify.finish ' , function ( ExerciseRunnerEvent $ e ) {
98
+ $ db = $ this -> getPDO (Path:: join ( $ e -> getContext ()-> getStudentExecutionDirectory (), ' db.sqlite ' ) );
119
99
120
- $ eventDispatcher ->listen (
121
- [
122
- 'cli.verify.reference-execute.fail ' ,
123
- 'verify.finish ' ,
124
- 'run.finish ' ,
125
- ],
126
- function () use ($ db ) {
100
+ try {
101
+ /** @var DatabaseExerciseCheck $exercise */
102
+ $ exercise = $ e ->getParameter ('exercise ' );
103
+ $ verifyResult = $ exercise ->verify ($ db );
104
+
105
+ if (false === $ verifyResult ) {
106
+ return Failure::fromNameAndReason ($ this ->getName (), 'Database verification failed ' );
107
+ }
108
+
109
+ return new Success ('Database Verification Check ' );
110
+ } finally {
127
111
unset($ db );
128
- $ this ->unlink ($ this ->userDatabasePath );
129
- $ this ->unlink ($ this ->solutionDatabasePath );
130
- rmdir ($ this ->databaseDirectory );
131
- },
132
- );
112
+ }
113
+ });
133
114
}
134
115
135
- private function unlink (string $ file ): void
116
+ private function getPDO (string $ path ): PDO
136
117
{
137
- if (file_exists ($ file )) {
138
- unlink ($ file );
139
- }
118
+ $ db = new PDO ('sqlite: ' . $ path );
119
+ $ db ->setAttribute (PDO ::ATTR_ERRMODE , PDO ::ERRMODE_EXCEPTION );
120
+
121
+ return $ db ;
140
122
}
141
123
}
0 commit comments