Parameter must be an array or an object that implements Countable with PHP 7.4? - count

I am getting "Parameter must be an array or an object that implements Countable" for this line of code:
$parent = $path[count($path)-1];
How would I rewrite it so it works?

This is because your $path variable is not an array type data structure or other iterable structure.
Without context, it is impossible to understand how you should rewrite this code.
You shouldn't run this piece of code without checking that the $path variable is not an array. This can be done by calling the is_array() function.
I also noticed that you are trying to get the last element of the array via count($path)-1. It is better to do this through the end() function, but you should pay attention to the fact that this function shifts the array pointer.

Related

Difference between big.Int and *big.Int, and how do you pass a big.Int by value

I can use methods like Text() on a big.Int and it works fine, but if I return a big.Int then use "myfunc().Text()" throws an error, whereas if I return a *big.Int, I get no error. Why can I use Text() on a big.Int, *big.Int, and on a function that returns *big.Int but not on a function whose return value is big.Int?
https://play.golang.org/p/ovgeQDHFstP
Based on this and other behavior (such as how it prints), it seems like *big.Int is the type that is intended for use, is that correct?
Also, if I make and use a variable of type big.Int or *big.Int, it is passed by reference. That's fine. But if I wanted to pass one by value, how is that best done?
Should I make a new big.Int and set it to the original value using Set() and pass that? Or should I pass the original big.Int in, and copy its value to a new big.Int using Set() inside the function? Or is there some other, better way of doing it?
The Text() method is defined for receiver type *big.Int, so obviously you can call it on variables of that type and on return values of functions returning *big.Int. You can also call it on variables of type big.Int, because Go automatically takes the address of a variable when you're trying to call its pointer methods, just to save you the trouble of typing an extra ampersand.
However you can't call it on return value of a function returning big.Int, because that value is not addressable. Here's what the spec says about addressability:
For an operand x of type T, the address operation &x generates a
pointer of type *T to x. The operand must be addressable, that is,
either a variable, pointer indirection, or slice indexing operation;
or a field selector of an addressable struct operand; or an array
indexing operation of an addressable array. As an exception to the
addressability requirement, x may also be a (possibly parenthesized)
composite literal.
Your return value is none of those things, so you can't use the pointer method any more than you can write foo := &myFunc(). To work around this, you could save the return value on a variable to make it addressable. But most likely your function should return a pointer in the first place.
Also note that there are no references in Go. Everything is passed by value, and pointers are values just like any other.
https://golang.org/pkg/math/big/ the Text() method has a pointer receiver, which means that you can only call a.Text() if a is *big.Int.
*big.Int is a pointer to big.Int, see https://play.golang.org/p/dD70b0tPeGp for a fixed version of your code

Error: forceDelete method doesnt exist

Can you please explain me the following behavior?
public function kill($id)
{
$post = Post::withTrashed()->where('id',$id)->get();
$post->forceDelete();
return redirect()->back()->with('success','Post Deleted Succesfully');
}
The code results in this error:
forceDelete method doesnt exist
But the following code does not.
public function kill($id)
{
$post = Post::withTrashed()->where('id',$id)->first();
$post->forceDelete();
return redirect()->back()->with('success','Post Deleted Succesfully');
}
Could someone explain?
My Laravel knowledge is rusty and I am a Rails guy. I just landed on your post to review it if it needs any improvements.
Any ways... if I am correct...
You have 2 functions, let them be F1 and F2 respectively.
In F1, you are searching for a post with a specific ID. What you get is a collection proxy and not a Post. Thus it doesn't respond to forceDelete().
In F2, you are asking for the first object in the collection thus you are getting a Post object that responds to forceDelete().
To delete a collection you will be able to use the delete() function instead in some thing like
Post::withTrashed()->where('id',$id)->delete();
Actually it is pretty simple. As you indicated in the post,
$post = Post::withTrashed()->where('id',$id)->get();
$post->forceDelete();
gives error
while
$post = Post::withTrashed()->where('id',$id)->first();
$post->forceDelete();
doesnot. The reason as for my understanding is, $id is always unique. And it returns a single data (or single model) related to that particular id from the post table. In the first case where you are using get() method, what you are trying to return is a multiple row data, hence it will return a single data but in multi-dimensional array format. You can check that using dd() helper function.
In second case, you are using first() method, it always returns single row of data related to that particular $id, so forceDelete() method exists for that case(In other sense you can say that forceDelete exists only the single row data model, but not multiple data row model which you are tying to retrieve using get(). Remember get() always tries to return multiple data, and multiple data can only be held on array, so it gives array as a result although the result is only one.)
Hope this helps.
This is because when you user ->get() what you really have is a Laravel collection and there is no forceDelete() method on collection.
So you have to use ->first() or ->find() and eloquent will return the model and then you can use forceDelete on it.
For example:
$users->each(function ($user) {
$user->forcedelete();
});
Is is efficient way of doing this is another question. But it works.

Strictly spy method argument in PHPUnit

I have this spy in a test:
$subject->expects( $this->once() )->method( 'send_json_success' )->with( $expected );
$expected is an array and one of the items of this array should be set to 0.
Instead is currently set to an empty string, which is the source of the problem I'm fixing.
I want to make sure the test fails when the item is set to an empty string, but I can't find how to tell PHPUnit to strictly check the array is exactly the same as $expected.
I can't use $this->same() because the method does not return anything: I need to test the method is called with the right arguments, instead.
As explained in the API documentation of the with() method, you can use a PHPUnit_Framework_Constraint object.
A PHPUnit_Framework_Constraint_IsIdentical object is used to implement the TestCase::assetSame() method.
So, it should be:
<?php
use PHPUnit_Framework_Constraint_IsIdentical;
// Test case class...
$subject->expects($this->once())
->method('send_json_success')
->with(new PHPUnit_Framework_Constraint_IsIdentical($expected));

Void pointer void type cast function call?

Ok this is odd. It's the first time I've seen such a line of code.
Basically this calls the entry point into an application once you've specified an offset (address) from a program's PE header.
As you can tell - I've been playing lately with writing my own PE loader. I'm still a beginner and attempting to understand the structure - but what exactly is that function call mean?
((void(*)(void))EntryPoint)();
//where 0x4484502 is gotten from:
PIMAGE_NT_HEADERS nt_header;
DWORD EntryPoint = nt_header->OptionalHeader.ImageBase + nt_header->OptionalHeader.AddressOfEntryPoint;
((void(*)(void))0x4484502)();
The line
((void(*)(void))0x4484502)();
Casts the integer 0x4484502 to a point to a function (starting at that address) that has void parameters and returns void. Once cast, the function pointer is called.
EDIT:
Just re-read the question.... replace 0x4484502 with EntryPoint does exactly the same thing... the variable EntryPoint is cast as a pointer to a function that has void params and returns void. Pointer then used to call function.
notation
(some_type)something
it is C-style cast. It is equal to a sequence of C++ casts, but without dynamic_cast so it is dangerous - it allows you to cast a pointer to private base to a pointer to derived class not only in derived class functions.
here we have
(void(*)(void))0x4484502
it means that 0x4484502 is casted to a pointer to a function that takes void and returns void.
the notation func_ptr()
means call the function pointed to by func_ptr.
you can always check such strange declarations on cdecl

element() vs. node() in XQuery

Can someone tell me the exact difference between node() and element() types in XQuery? The documentation states that element() is an element node, while node() is any node, so if I understand it correctly element() is a subset of node().
The thing is I have an XQuery function like this:
declare function local:myFunction($arg1 as element()) as element() {
let $value := data($arg1/subelement)
etc...
};
Now I want to call the function with a parameter which is obtained by another function, say functionX (which I have no control over):
let $parameter := someNamespace:functionX()
return local:myFunction($parameter)
The problem is, functionX returns an node() so it will not let me pass the $parameter directly. I tried changing the type of my function to take a node() instead of an element(), but then I can’t seem to read any data from it. $value is just empty.
Is there some way of either converting the node to an element or should am I just missing something?
EDIT: As far as I can tell the problem is in the part where I try to get the subelement using $arg1/subelement. Apparently you can do this if $arg1 is an element() but not if it is a node().
UPDATE: I have tested the example provided by Dimitre below, and it indeed works fine, both with Saxon and with eXist DB (which is what I am using as the XQuery engine). The problem actually occurs with the request:get-data() function from eXist DB. This function gets data provided by the POST request when using eXist through REST, parses it as XML and returns it as a node(). But for some reason when I pass the data to another function XQuery doesn’t acknowledge it as being a valid element(), even though it is. If I extract it manually (i.e. copy the output and paste it to my source code), assign it to a variable and pass it to my function all goes well. But if I pass it directly it gives me a runtime error (and indeed fails the instance of test).
I need to be able to either make it ignore this type-check or “typecast” the data to an element().
data() returning empty for an element just because the argument type is node() sounds like a bug to me. What XQuery processor are you using?
It sounds like you need to placate static type checking, which you can do using a treat as expression. I don't believe a dynamic test using instance of will suffice.
Try this:
let $parameter := someNamespace:functionX() treat as element()
return local:myFunction($parameter)
Quoting from the 4th edition of Michael Kay's magnum opus, "The treat as operator is essentially telling the system that you know what the runtime type is going to be, and you want any checking to be deferred until runtime, because you're confident that your code is correct." (p. 679)
UPDATE: I think the above is actually wrong, since treat as is just an assertion. It doesn't change the type annotation node(), which means it's also a wrong assertion and doesn't help you. Hmmm... What I really want is cast as, but that only works for atomic types. I guess I'm stumped. Maybe you should change XQuery engines. :-) I'll report back if I think of something else. Also, I'm curious to find out if Dimitre's solution works for you.
UPDATE #2: I had backpedaled here earlier. Can I backpedal again? ;-) Now my theory is that treat as will work based on the fact that node() is interpreted as a union of the various specific node type annotations, and not as a run-time type annotation itself (see the "Note" in the "Item types" section of the XQuery formal semantics.) At run time, the type annotation will be element(). Use treat as to guarantee to the type checker that this will be true. Now I wait on bated breath: does it work for you?
EXPLANATORY ADDENDUM: Assuming this works, here's why. node() is a union type. Actual items at run time are never annotated with node(). "An item type is either an atomic type, an element type, an attribute type, a document node type, a text node type, a comment node type, or a processing instruction type."1 Notice that node() is not in that list. Thus, your XQuery engine isn't complaining that an item has type node(); rather it's complaining that it doesn't know what the type is going to be (node() means it could end up being attribute(), element(), text(), comment(), processing-instruction(), or document-node()). Why does it have to know? Because you're telling it elsewhere that it's an element (in your function's signature). It's not enough to narrow it down to one of the above six possibilities. Static type checking means that you have to guarantee—at compile time—that the types will match up (element with element, in this case). treat as is used to narrow down the static type from a general type (node()) to a more specific type (element()). It doesn't change the dynamic type. cast as, on the other hand, is used to convert an item from one type to another, changing both the static and dynamic types (e.g., xs:string to xs:boolean). It makes sense that cast as can only be used with atomic values (and not nodes), because what would it mean to convert an attribute to an element (etc.)? And there's no such thing as converting a node() item to an element() item, because there's no such thing as a node() item. node() only exists as a static union type. Moral of the story? Avoid XQuery processors that use static type checking. (Sorry for the snarky conclusion; I feel I've earned the right. :-) )
NEW ANSWER BASED ON UPDATED INFORMATION: It sounds like static type checking is a red herring (a big fat one). I believe you are in fact not dealing with an element but a document node, which is the invisible root node that contains the top-level element (document element) in the XPath data model representation of a well-formed XML document.
The tree is thus modeled like this:
[document-node]
|
<docElement>
|
<subelement>
and not like this:
<docElement>
|
<subelement>
I had assumed you were passing the <docElement> node. But if I'm right, you were actually passing the document node (its parent). Since the document node is invisible, its serialization (what you copied and pasted) is indistinguishable from an element node, and the distinction was lost when you pasted what is now interpreted as a bare element constructor in your XQuery. (To construct a document node in XQuery, you have to wrap the element constructor with document{ ... }.)
The instance of test fails because the node is not an element but a document-node. (It's not a node() per se, because there's no such thing; see explanation above.)
Also, this would explain why data() returns empty when you tried to get the <subelement> child of the document node (after relaxing the function argument type to node()). The first tree representation above shows that <subelement> is not a child of the document node; thus it returns the empty sequence.
Now for the solution. Before passing the (document node) parameter, get its element child (the document element), by appending /* (or /element() which is equivalent) like this:
let $parameter := someNamespace:functionX()/*
return local:myFunction($parameter)
Alternatively, let your function take a document node and update the argument you pass to data():
declare function local:myFunction($arg1 as document-node()) as element() {
let $value := data($arg1/*/subelement)
etc...
};
Finally, it looks like the description of eXist's request:get-data() function is perfectly consistent with this explanation. It says: "If its not a binary document, we attempt to parse it as XML and return a document-node()." (emphasis added)
Thanks for the adventure. This turned out to be a common XPath gotcha (awareness of document nodes), but I learned a few things from our detour into static type checking.
This works perfectly using Saxon 9.3:
declare namespace my = "my:my";
declare namespace their = "their:their";
declare function my:fun($arg1 as element()) as element()
{
$arg1/a
};
declare function their:fun2($arg1 as node()) as node()
{
$arg1
};
my:fun(their:fun2(/*) )
when the code above is applied on the following XML document:
<t>
<a/>
</t>
the correct result is produced with no error messages:
<a/>
Update:
The following should work even with the most punctuential static type-checking XQuery implementation:
declare namespace my = "my:my";
declare namespace their = "their:their";
declare function my:fun($arg1 as element()) as element()
{
$arg1/a
};
declare function their:fun2($arg1 as node()) as node()
{
$arg1
};
let $vRes := their:fun2(/*)
(: this prevents our code from runtime crash :)
return if($vRes instance of element())
then
(: and this assures the static type-checker
that the type is element() :)
my:fun(their:fun2(/*) treat as element())
else()
node() is an element, attribute, processing instruction, text node, etc.
But data() converts the result to a string, which isn't any of those; it's a primitive type.
You might want to try item(), which should match either.
See 2.5.4.2 Matching an ItemType and an Item in the W3C XQuery spec.
Although it's not shown in your example code, I assume you are actually returning a value (like the $value you are working with) from the local:myFunction.

Resources