Updating legacy php 5 code to php 7 / moodle 3.5

Updating legacy php 5 code to php 7 / moodle 3.5

by Dan Baker -
Number of replies: 34

Hi  I  have  been  trying  to   update  a legacy  plugin  ( tutorship )   so  that  it  will work  again in  Moodle 3.5

To bring it back to life   I   need o   update  the  code  so it  is  php 7  compatible.   Being a  novice  programmer I  am  battling through  and   have  come   across this  latest  error   being  shown   with debugging on in Moodle  3.5 :

Exception - Argument 3 passed to moodle_database::get_field() must be of the type array, string given, called in 


The relevant  code   seems  to be :

 $studentid   = $USER->id; //$DB->get_field('tutorship_reserves', 'studentid', $reservationconditions);

                $reserveid   = $DB->get_field('tutorship_reserves', 'timetableid', $timetableid2);


I can see in the Moodle  tracker  this  kind  of  error  has  been  experienced  before:    https://tracker.moodle.org/browse/MDL-15004 

I am  really not  sure  how   to  deal with this  problem.   Can  someone  suggest  a  way  to  fix  it ? 

BR  

Dan 

Average of ratings: -
In reply to Dan Baker

Updating legacy code

by Marcus Green -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
The signature of the get_field() function is $DB->get_field($table, $return, array $conditions, $strictness=IGNORE_MISSING) According to this URL https://docs.moodle.org/dev/Data_manipulation_API#get_field However it is always best to search through the source to confirm these things and I think this is the actual source where the comment above it should help understand how to use it.

https://github.com/moodle/moodle/blob/f3507273e9f5eb05bb05c24f390d2fc8bf2dcf0a/lib/dml/moodle_database.php#L1602
I suspect that a string is being passed and is caught by the function now having type checking. So update the call so an array is passed in. If that is not clear, please ask again. Good luck with your development.
In reply to Dan Baker

Re: Updating legacy php 5 code to php 7 / moodle 3.5

by AL Rachels -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers

Hi Dan,

Got a link to the plugin or the code on github? Helps to be able to see the preceding code.

In reply to AL Rachels

Re:

by Dan Baker -
The original plugin is on github https://github.com/collisiondetection/moodle-mod-tutorship.

I can show you some of our customizations . This is the offending block of code :


// Make reservation
} else if (tutorship_can_reserve($timetableid, $course->id, $USER->id)) { // Has made all possible reservations?

$calc_timeslotid = (int) $DB->get_field('tutorship_timetables', 'timeslotid', array('id' => $timetableid));

$calc_timeslot = $DB->get_record('tutorship_timeslots', array('id' => $calc_timeslotid));
$yearData = tutorship_get_YearFromWeek($_SESSION['today'] , $calc_timeslot->day);
$yearData = date('Y');

// if booking for particular slot is done by student done insert again
$searchBooking = $DB->get_records_sql("SELECT * FROM {tutorship_reserves} WHERE timetableid = $timetableid");
// if booking for same slot already record exist dont update again
// if(count($searchBooking) == 0) {

/* ==================== INSERT TUTORSHIP RESERVES: START ==================== */
In reply to AL Rachels

Re: Updating legacy php 5 code to php 7 / moodle 3.5

by Dan Baker -
The original plugin is on github https://github.com/collisiondetection/moodle-mod-tutorship .  We have been heavily customizing it so I can currently share the offending block of code :


// Make reservation } else if (tutorship_can_reserve($timetableid, $course->id, $USER->id)) { // Has made all possible reservations?
$calc_timeslotid = (int) $DB->get_field('tutorship_timetables', 'timeslotid', array('id' => $timetableid));
$calc_timeslot = $DB->get_record('tutorship_timeslots', array('id' => $calc_timeslotid)); $yearData = tutorship_get_YearFromWeek($_SESSION['today'] , $calc_timeslot->day); $yearData = date('Y');
// if booking for particular slot is done by student done insert again $searchBooking = $DB->get_records_sql("SELECT * FROM {tutorship_reserves} WHERE timetableid = $timetableid"); // if booking for same slot already record exist dont update again // if(count($searchBooking) == 0) {
/* ==================== INSERT TUTORSHIP RESERVES: START ==================== */

Thanks,

Dan 
In reply to Dan Baker

Re: Updating legacy php 5 code to php 7 / moodle 3.5

by Dan Baker -
It seems this is actually the problem area :

 // If teacher has automatic confirmation enabled, confirm and email to student
            //if ($DB->get_field('tutorship_configs', 'autoconfirm', array('teacherid' => $teacherid))) 
			{
                if ($DB->set_field('tutorship_reserves', 'confirmed', 1, $reservationconditions)) {
$sectiontitle = ''; $studentid = $USER->id; //$DB->get_field('tutorship_reserves', 'studentid', $reservationconditions); $reserveid = $DB->get_field('tutorship_reserves', 'timetableid', $timetableid2);

In reply to Dan Baker

Re:

by Marcus Green -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
I would change it to read
$studentid = $USER->id; //$DB->get_field('tutorship_reserves', 'studentid', $reservationconditions);
var_dump($timetableid2);
exit();
Switch to clean theme then rerun.
Chances are that $timetable2 is not an array (might be a string, number or null).
Better still install MS VSCode and xdebug (all free software), then step through the code line by line watching the values.
In reply to Marcus Green

Re: Moodle in English:

by Dan Baker -
Yes  I  will  try  this   and    I   have  Netbeans and  VS  set  up   

with xdebug.
In reply to Dan Baker

Re:

by Marcus Green -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
I was a big fan of NetBeans for about 10 years and resisted the lure of VSEdit, but I am now a big fan, especially its excellent integration with Xdebug.
In reply to Marcus Green

Re: Moodle in English:

by Dan Baker -
I  used  your var   dump  code and  it  came  back : 

/var/www/moodle/mod/tutorship/view.php:1074:string '3' /(length=1) .   

/Will try stepping through the code next.



/



/
In reply to Marcus Green

Re:

by Dan Baker -
xdebug errorManaged to to get xdebug working and this is what it shows:

In reply to Dan Baker

get_field parameter error

by Marcus Green -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
So that confirms the cause of the message, the function was expecting an array but was receiving a string. So the next step is to work out what the array should be like, the error might just go away (and possibly fix it) by changing the call to include

$reserveid = $DB->get_field('tutorship_reserves', 'timetableid', [$timetableid2]);

(Note the square braces that will convert the string to an array so the function will receive something like [0=>'3'] instead of just '3', the index of the array, (the zero in that array) may not matter as get_field probably just walks through the elements and looks at the values)

But now you have xdebug working you should trace through the call to see exactly what is happening. If you click to create a 'break' just before that code then trace into it you will see what get_field is likely to do in it. The other good way is to read through the source of get_field and find out how it uses that parameter
Average of ratings: Useful (1)
In reply to Marcus Green

Re: Updating legacy php 5 code to php 7 / moodle 3.5

by Andreas Grabs -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Translators

Hi,
in my opinion the array must look like
["timetableid" => $timetableid2]
Otherwise there is no information what the value "$timetableid2" should be compared with.
Best regards
Anders

Average of ratings: Useful (1)
In reply to Andreas Grabs

Re:

by Marcus Green -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
Hi Andreas, you may well be right, I didn't read the code fully smile. I assure everyone I read it very carefully in my day job ....
Average of ratings: Useful (1)
In reply to Marcus Green

Re:

by Dan Baker -
I tried both ways but no luck:
Attachment error  after  adjusting  array.png
In reply to Dan Baker

Re:

by Andreas Grabs -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Translators

Hi Dan,

maybe you take a look in your plugins table definition. There you should find the right name of the column which is needed. The column "timetableid" was just a guess.

Best regards Andrews

In reply to Andreas Grabs

Re:

by Dan Baker -
That column  definitely exists :




I see no  $timetabletableid2  - Is this the area to look at more? 
In reply to Dan Baker

Re:

by Davo Smith -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Looking at what you are asking for on those 2 lines:

$reserveid = $DB->get_field('tutorship_reserves', 'timetableid', [$timetableid2]);

This means - look in the table 'mdl_tutorship_reserves' and return the value of the field 'timetableid', where the field '0' has the value stored in $timestableid2.

$reserveid = $DB->get_field('tutorship_reserves', ["timetableid" => $timetableid2]);

This means - look in the table 'mdl_tutorship_reserves' and return the value of the field ["timetableid" => $timetableid2], which matches an unspecified condition.

What you want to do is the following:

$reserveid = $DB->get_field('NAME OF TABLE', 'NAME OF FIELD TO RETURN', ['NAME OF FIELD TO MATCH' => 'VALUE OF FIELD TO MATCH']);

As I don't know exactly what you are trying to match or what you are trying to retrieve, it is easiest if I give a different example - if I wanted to retrieve the first name of a user with a username matching $username, I would write:

$firstname = $DB->get_field('user', 'firstname', ['username' => $username]);

I hope that clarifies what is needed to fix your code.

Average of ratings: Useful (1)
In reply to Davo Smith

Re:

by Dan Baker -
Thanks for the clarification.

I will post some examples where this mysterious $timetableid2 is being used so we get a better understanding of the code.

I was hoping to opensource the code on version 2 but may have to do it sooner to actually get the plugin built.
In reply to Dan Baker

Re:

by Marcus Green -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
"I was hoping to opensource the code on version 2 but may have to do it sooner to actually get the plugin built."
One of the 'hidden' benefit of releasing the code. Last year someone contributed some really nice code to one of my question type plugins.
In reply to Marcus Green

Re:

by Dan Baker -
Does this make it easier to understand what is being matched :



In reply to Dan Baker

Re:

by Dan Baker -
$reserveid = $DB->get_field('NAME OF TABLE', 'NAME OF FIELD TO RETURN', ['NAME OF FIELD TO MATCH' => 'VALUE OF FIELD TO MATCH']);

In the code I showed $timetableid2 is a super global and not a field ?
In reply to Dan Baker

Re:

by Dan Baker -
Wow this seemed to work :

$reserveid = $DB->get_field('tutorship_reserves', 'timetableid', ['timetableid' => $timetableid2]);




surprise
In reply to Dan Baker

Re:

by Marcus Green -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
Looks good, by the way If you have the debugger set up you can get the code to break and then call arbitrary function in it to see what the result it. I was doing that only yesterday with various $DB->get_blabla calls.
In reply to Marcus Green

Re:

by Dan Baker -
Thanks for the tip. I was going to ask what other tools do you use. I see in moodle 3.7 there is the ability to log SQL queries but not in 3.5 :

$CFG->dboptions = array (
//'logall' => true,
'logslow' => 5,
'logerrors' => true,
);

This is what I am currently doing :

xdebug
debug on in moodle
var_dump
print_r
tail -f /var/xdebug.log
tail -f /var/log/apache2/error.log


Is there anything else I can use ?
In reply to Dan Baker

Re:

by Marcus Green -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
You are ahead of me, tail -f/var/xdebug.log looks like a good idea
(this is stuff for the dev wiki I think)
Average of ratings: Useful (1)
In reply to Marcus Green

Re:

by Dan Baker -
Ok will check the wiki . tail -f is really good for seeing the messages going into the log in real time.

Might have some more questions and will start new threads.
In reply to Dan Baker

Re:

by Marcus Green -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
Actually I meant you could add it to the wiki. Anyone can edit it and update it. I think you need to login separately to the forums. Places where it might go are
https://docs.moodle.org/dev/Setting_up_development_environment
or somwhere here
https://docs.moodle.org/dev/Category:Developer_tools
It would be really cool if you were to add a bit of text and post the link here.
In reply to Marcus Green

Re:

by Dan Baker -
Great advice I will try this.
In reply to Dan Baker

Re:

by Marcus Green -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
This thread describes a good example of the standard Moodle development/debug/fix process.
In reply to Marcus Green

Re:

by Dan Baker -
Yeah, will be a learning curve for me.

I was able to catch some standard php upgrade errors using https://github.com/Alexia/php7mar

such as @mysql to myqli

Does moodle provide such a checker that can offer some automated assistance? Otherwise I will have to dissect the code really carefully.
In reply to Dan Baker

Re:

by Marcus Green -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
"Does Moodle provide such a checker that can offer some automated assistance? Otherwise I will have to dissect the code really carefully."
Sadly no, in my experience it typically takes a small number of days to upgrade a plugin.